【发布时间】:2015-11-22 11:44:32
【问题描述】:
The Little Schemer 中的The Third Commandment 表示:
在构建列表时,先描述第一个典型元素,然后将其用于自然递归。
“自然递归”的确切定义是什么?我问的原因是因为我正在上丹尼尔弗里德曼的编程语言原则课程,并且以下代码不被认为是“自然递归”:
(define (plus x y)
(if (zero? y) x
(plus (add1 x) (sub1 y))))
但是,以下代码被认为是“自然递归的”:
(define (plus x y)
(if (zero? y) x
(add1 (plus x (sub1 y)))))
我更喜欢“非自然递归”代码,因为它是尾递归的。然而,这样的代码被认为是诅咒。当我问为什么我们不应该以尾递归形式编写函数时,助理教练简单地回答说:“你不要弄乱自然递归。”
把函数写成“自然递归”的形式有什么好处?
【问题讨论】:
-
也许自然递归是一个递归过程。因此实际上不是关于过程递归,而是算法的性质。您的第一个示例是递归过程,但不是递归过程,因为空间是恒定的。这实际上是一个迭代过程。迭代过程可以使用显式数据结构而不是系统堆栈来实现递归过程。有时递归过程中的递归过程更容易阅读。
-
“这样的代码被视为诅咒”。是否有人明确告诉过你,或者这是你的印象?
-
虽然我不确定它是否值得回答,但作者可能受到了"primitive recursion" from computability theory 的影响。关键在于接受“复合”参数(例如
h(S(y)))的函数将由g(y,h(y))定义。将列表视为列表时,对应的对象将更类似于h(cons(x,y)) = g(x,h(y))。这不是很准确,但可能有一些影响。 -
部分好处是原始递归函数总是停止。证明弗里德曼所说的“自然递归”函数终止会更容易,因为可以更容易地证明每个递归都朝着基本情况移动。
-
@AaditMShah 我完全同意效率。我的观点更多地是关于证明某事是正确实现的难易程度。当我们定义
reverse(x::xs) == append(reverse(xs),[x])时,我们会得到一个非常简单的归纳证明,只要我们知道 append 有效。我们可以立即看到 x 是最后一个元素,等等。累加器版本更有效,但证明它是正确可能有点困难(至少在入门级别)。我完全赞成编写更高效的代码;我只是在猜测作者为什么叫...
标签: recursion scheme lisp racket the-little-schemer