我认识到输出中有一个明显的模式,我只是想知道为什么当我尝试运行任何东西时,lispbox的REPL会中止> 52.此外,关于改进守则的任何建议都非常受欢迎。 ^ - ^
(defun count-reduced-fractions (n d sum)
(setf g (gcd n d))
(if (equal 1 d)
(return-from count-reduced-fractions sum)
(if (zerop n)
(if (= 1 g)
(count-reduced-fractions (1- d) (1- d) (1+ sum))
(count-reduced-fractions (1- d) (1- d) sum))
(if (= 1 g)
(count-reduced-fractions (1- n) d (1+ sum))
(count-reduced-fractions (1- n) d sum)))))
我打电话时所得到的一切
(count-reduced-fractions 53 53 0)
是
;评估中止
对我来说没有多大意义,考虑到它会在下面的所有数字上运行(并返回准确的结果),并且我可以(如果我想的话)在我的脑海中做到53,在纸上,或者在lisp中一次一行。我甚至测试了许多大于53的不同数字,以确保它不是特定于53.没有任何作用。
答案 0 :(得分:6)
此行为暗示缺少尾调用优化,因此您的递归会破坏堆栈。可能的原因是您已声明调试优化。
顺便说一句,您不需要显式调用return-from
。由于sum
是一个自我评估的符号,因此您可以更改此行
(return-from count-reduced-fractions sum)
到
sum
编辑:建议更改的解释:“sum”计算其自己的值,该值成为“if”语句的返回值,因为这是defun中的最后一个语句)成为函数的返回值。
编辑:声明优化的说明:您可以将以下内容添加到您的顶级:
(declaim (optimize (speed 3)
(debug 0)))
或使用相同的内容,但使用declare
代替declaim
作为函数中的第一个语句。如果它不起作用,你也可以尝试(空格3)和(安全0)。
尾调用优化意味着直接返回其返回值的函数调用被转换为堆栈上的帧替换(而不是堆叠),有效地“展平”对循环的递归函数调用,并消除递归函数调用。这使得调试变得更加困难,因为没有函数调用可以在你期望的地方调用。你不知道如何“深入”一个递归错误发生(就像你已经编写了一个循环开始)。您的环境可能会生成一些必须覆盖的默认声明以启用TCO。
编辑:只是重新审视这个问题,g
是什么?我认为你其实想要
(let ((g (gcd n d)))
;; ...
)
答案 1 :(得分:3)
我的猜测是lispbox有一个内置的堆栈深度限制。由于Common Lisp不保证尾递归函数使用常量堆栈空间,因此每次调用count-reduced-fractions可能会在堆栈上添加另一层。
顺便说一下,SBCL运行这个算法没有问题。
* (count-reduced-fractions 53 53 0)
881
* (count-reduced-fractions 100 100 0)
3043
答案 2 :(得分:1)
作为一种风格问题,你可以选择d和sum。
(defun test (n &optional (d n) (sum 0)) .. )
答案 3 :(得分:0)
可能是Stack Overflow(嘿)。