与项目euler 72(lisp)相关的奇怪问题

与项目euler 72(lisp)相关的奇怪问题,lisp,common-lisp,Lisp,Common Lisp,我认识到在这个输出中有一个明显的模式,我只想知道当我尝试运行>52的任何东西时,为什么lispbox的REPL会中止。此外,任何关于改进代码的建议都是非常受欢迎的^-^ (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 (=

我认识到在这个输出中有一个明显的模式,我只想知道当我尝试运行>52的任何东西时,为什么lispbox的REPL会中止。此外,任何关于改进代码的建议都是非常受欢迎的^-^

(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)))))
我打电话时得到的一切

(计算减少分数53 53 0)

);评估中止


这对我来说没有多大意义,因为它会在下面的所有数字上运行(并返回准确的结果),而且我可以(如果我愿意的话)在脑海中、纸上或lisp中一次一行地执行53。我甚至对许多大于53的不同数字进行了测试,以确保它不是针对53的。什么都不起作用。

可能是堆栈溢出(呵呵)。

我猜lispbox有一个内置的堆栈深度限制。由于Common Lisp不能保证尾部递归函数使用恒定的堆栈空间,因此每次调用计数缩减分数都可能在堆栈上添加另一层

顺便说一下,SBCL运行这个算法没有问题

* (count-reduced-fractions 53 53 0)
881

* (count-reduced-fractions 100 100 0)
3043

作为一种风格,你可以选择d和sum

(defun test (n &optional (d n) (sum 0)) .. )

这种行为暗示缺少尾部调用优化,因此递归会破坏堆栈。一个可能的原因是您已经声明了调试优化

顺便说一下,您不需要从显式调用
返回。由于
sum
是一个自评估符号,因此可以更改此行

(return-from count-reduced-fractions sum)

编辑:对建议更改的解释:“sum”计算为其自身的值,该值将成为“if”语句的返回值,而“if”语句(因为这是defun中的最后一条语句)将成为函数的返回值

编辑:解释declaimed优化:您可以将以下内容添加到顶层:

(declaim (optimize (speed 3)
                   (debug 0)))
或者使用相同的语句,但将
declare
而不是
declaim
作为函数中的第一个语句。如果不起作用,您也可以尝试(空格3)和(安全0)

尾部调用优化意味着直接返回其返回值的函数调用被转换为堆栈上的帧替换(而不是堆叠),有效地“展平”循环的递归函数调用,并消除递归函数调用。这使得调试更加困难,因为在您期望的地方没有函数调用。您不知道错误发生在递归中的“深度”(就像您刚开始编写循环一样)。您的环境可能会做出一些默认声明,您必须覆盖这些声明才能启用TCO

编辑:再问一下这个问题,什么是
g
?我想你真的想

(let ((g (gcd n d)))
  ;; ...
  )

你的文字中的点表示什么吗?它就像一张笑脸。^-^,^ ^ ^,:),等等。我知道表情符号,我指的是“…”点。在我看来,它们就像是一个人在说“我没有一个眨眼的线索”时的停顿。这是一个坏习惯,真的,即使我知道我在说什么,我也会这样做……这似乎延长了函数的寿命,但不会太长。
(let ((g (gcd n d)))
  ;; ...
  )