[英]Racket: Identifying tail recursion?
我在球拍中写了两个不同的函数来确定数字列表是否在升序:
(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”语句中的第二个参数返回,然后才能评估布尔表达式。 相反,对于升序尾助手,我能够在进行递归调用之前评估小于表达式。
这是正确的,还是让自己比以前更加困惑?
DrRacket可以帮助您确定呼叫是否处于尾部位置。 单击“语法检查”按钮。 然后将鼠标指针移动到相关表达式的左括号。 在你的例子中,我得到了这个:
紫色箭头表示表达式处于尾部位置。
从手册:
尾调用:通过从尾表达式到其周围表达式绘制浅紫色箭头来注释(语法上)相对于其封闭上下文在尾部位置的任何子表达式。
你是正确的,在第一个版本的递归调用返回and
,而在第二个版本的递归调用是一个尾调用。
但是, and
是一个宏,并且通常使用if
扩展
(define (ascending list)
(if (<= (length list) 1)
#t
(if (< (car list) (car (cdr list)))
(ascending (cdr list))
#f)))
这是尾递归。
函数处于尾部位置的要求之一是它的返回值可用作父函数的返回值而无需修改或检查。 也就是说,在评估尾部位置语句之后,父函数应该能够立即返回。
在第一次出现时,您的第一个功能,检查升序的返回值。 似乎不会返回提升的价值,而是从它中获得的价值。 然而 ,根据该规范R5RS的相关部分,在一个最终表达式和语句是在尾部位置。 (当我清醒的时候,我知道这一点)
所以你错了。
http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-ZH-6.html#%_sec_3.5
(注意:编辑以纠正我最初仓促的答案)。
关于尾部位置的 Racket 文档说明了检查尾部位置和不匹配的位置是特定形式的文档:
尾部位置规范提供了关于计算的渐近空间消耗的保证。 一般来说,尾部位置的规范与每种句法形式一致,
if
。
Racket的文档和说(重点补充):
(and expr ...)
如果没有提供exprs,则结果为#t。
如果提供了单个expr,那么它处于尾部位置 ,因此和表达式的结果是expr的结果。
否则,将评估第一个expr。 如果它产生#f,则表达式的结果为#f。 否则,结果与a和表达式相同,其中尾部位置的剩余exprs相对于原始和形式。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.