我喜欢随时使用递归,这似乎是一种更自然的循环方式然后实际循环。我想知道在lisp中递归是否有任何限制?就像在python中那样,在1000次循环后它会变得怪异吗? 你可以用它来说游戏循环吗?
现在测试一下,简单计算递归函数。现在在> 7000000!
非常感谢
答案 0 :(得分:11)
Scheme要求尾部调用优化,一些CL实现也提供它。但是,CL并没有强制要求它。
请注意,要使尾调用优化起作用,您需要确保不必返回。例如。一个简单的Fibonacci实现,需要返回并添加到另一个递归调用,不会进行尾调用优化,因此你将耗尽堆栈空间。
答案 1 :(得分:9)
首先,您应该了解尾调用的内容。
尾调用是不消耗堆栈的调用。 现在你需要识别消耗堆栈的时间。
我们来看一个阶乘的例子:
(defun factorial (n)
(if (= n 1)
1
(* n (factorial (- n 1)))))
这是阶乘的非尾递归实现。 为什么?这是因为除了从阶乘返回之外,还有待计算。
(* n ..)
因此,每次调用阶乘时,您都会堆叠n。 现在让我们编写尾递归因子:
(defun factorial-opt (n &key (result 1))
(if (= n 1)
result
(factorial-opt (- n 1) :result (* result n))))
这里,结果作为参数传递给函数。 所以你也在使用堆栈,但区别在于堆栈大小保持不变。 因此,编译器可以通过仅使用寄存器并将堆栈留空来优化它。
factorial-opt
然后更快,但可读性更低。
factorial
仅限于factorial-opt
不匹配的堆栈大小。
所以你应该学习识别尾递归函数,以便知道递归是否有限。
可能有一些编译器技术将非尾递归函数转换为尾递归函数。也许有人可以在这里指出一些链接。