我在球拍中写了两个不同的函数来确定数字列表是否在升序:
(define (ascending list)
(if (<= (length list) 1)
#t
(and (< (car list) (car (cdr list))) (ascending (cdr list)))))
(define (ascending-tail list)
(ascending-tail-helper #t list))
(define (ascending-tail-helper prevBool rest)
(if (<= (length rest) 1)
prevBool
(ascending-tail-helper (and prevBool (< (car rest) (car (cdr rest)))) (cdr rest))))
我最难确定第一次提升是否是尾递归,所以我用我认为的尾递归重写了它。
我回顾性地认为第一个不是尾递归的原因是我相信在每个递归级别,函数将等待“and”语句中的第二个参数返回,然后才能评估布尔值表达。相反,对于升序尾助手,我可以在进行递归调用之前评估小于表达式。
这是正确的,还是让自己比以前更加困惑?
答案 0 :(得分:7)
DrRacket可以帮助您确定呼叫是否处于尾部位置。单击“语法检查”按钮。然后将鼠标指针移动到相关表达式的左括号。在你的例子中,我得到了这个:
紫色箭头表示表达式处于尾部位置。
从手册:
尾调用:任何(语法上)的子表达式 关于其封闭上下文的尾部位置由注释 从尾部表达到它的一个浅紫色箭头 周围的表达。
答案 1 :(得分:6)
你是对的,在第一个版本中,递归调用返回and
,而在第二个版本中,递归调用是尾调用。
但是,and
是一个宏,通常使用if
(define (ascending list)
(if (<= (length list) 1)
#t
(if (< (car list) (car (cdr list)))
(ascending (cdr list))
#f)))
这是尾递归。
答案 2 :(得分:0)
函数处于尾部位置的要求之一是它的返回值可用作父函数的返回值而无需修改或检查。也就是说,在评估尾部位置语句之后,父函数应该能够立即返回。
首次出现时,您的第一个功能是检查升序的返回值。它似乎不会返回升序的值,而是返回从它派生的值。 但,根据R5RS规范的相关部分,和语句中的最终表达式为尾部位置。 (当我清醒的时候,我知道这一点)
所以你错了。
http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-6.html#%_sec_3.5
(注意:编辑以纠正我最初仓促的答案)。
答案 3 :(得分:0)
球拍的documentation on tail positions表示检查尾部位置和不具备位置的地方是特定表格的文档:
尾部位置规范提供了关于计算的渐近空间消耗的保证。一般来说,尾部位置的规范与每个句法形式一致,如
if
。
Racket's docs on and说(强调补充):
(and expr ...)
如果没有提供exprs,则结果为#t。
如果提供了单个expr,那么它处于尾部位置,所以 和表达式的结果是expr的结果。
否则,将评估第一个expr。如果它产生#f,结果 和表达式是#f。 否则,结果与a相同 并且在尾部位置表达剩余的exprs 原始和形式。