【问题标题】:Invocation Stack History Overflow调用堆栈历史溢出
【发布时间】:2014-05-25 21:03:06
【问题描述】:

一直在课堂上玩 LISP。这无疑是我编写的第一个 LISP 代码。我无法弄清楚为什么对于函数(longest_collatz n) 的输入值超过 2000 时,此代码会产生错误"invocation stack history overflow"。有更多这门语言经验的人可以帮助我理解错误吗?

(defun longest_collatz(n)
  (reverse 
   (maxlist
    (loop for x from 1 to n
       collect (list x (length (collatz x)))))))

(defun collatz (n)
  (if (<= n 1)
      '(1)
      (if (= (mod n 2) 0)
          (cons (/ n 2) (collatz (/ n 2)))
          (cons (+ (* n 3) 1) (collatz (+ (* n 3) 1))))))

(defun maxlist (z)
  (if (> (length z) 1)
      (if (< (cadr (elt z 0)) (cadr (elt z 1)))
          (maxlist (cdr z))
          (maxlist (cons (elt z 0) (cddr z))))
      (car z)))

【问题讨论】:

  • 这似乎不适合我。 (longest_collatz 3000)SBCL 1.1.11 上返回 (217 2919)。您使用的是什么实现和版本?一般来说,Common Lisp 不能保证消除尾调用,所以我认为您的 maxlist 将是较大输入的问题。
  • 您能告诉我们您的功能应该做什么吗?逐一描述。显示输入和输出。

标签: stack lisp stack-overflow history invocation


【解决方案1】:

Youtcollatz 函数不是尾递归的,所以即使你编译你的代码也不太可能转化为循环。

您可以使用累加器对其进行重写,以便编译器将其转换为循环:

(defun collatz (n &optional acc)
  (unless (plusp n)
    (error "~s(~s): positive argument is required" 'collatz n))
  (if (= n 1)
      (nreverse (cons 1 acc))
      (let ((next (if (evenp n)
                      (ash n -1) ; same as (mod n 2)
                      (1+ (* n 3)))))
        (collatz next (cons next acc)))))

(这是一个 bug-for-bug 的重新实现)。

注意事项:

  1. 避免elt;使用firstsecond 会更好。
  2. 使用reduce 重写maxlist 会使其更快更清晰。

【讨论】:

  • 这只是问题的一部分。你能让整个事情尾递归吗?而且没有任何理由返回实际列表;只要长度就足够了。
  • @ooga:我做了collatz TR; maxlist 已经是 TR,但应重写以使用 reduce 以提高速度和清晰度(并避免欺骗)。
  • 我在考虑longest_collatz 函数。您的整个解决方案是否适用于数十亿的数字? (longest_collatz 1000000000) 的答案是什么?
  • @ooga: longest_collatz 不递归。此外,此功能不能很好地扩展。但是,(longest_collatz 100000) 需要 11 秒并返回 (351 77031)
  • 你是对的!我最初读错了,虽然它也递归了,但它只是调用collatz。但是您返回的列表并不完全正确,因为它没有第一个数字并且包含 1 两次(尽管我相信这就是 OP 的原始代码所做的)。如果您只计算元素而不是返回列表,它可能会运行得更快。
【解决方案2】:

这是一个只返回 collat​​z 列表的长度而不是列表本身的函数。它可能更有效(并且是尾递归的)。

(defun collatz_length2 (n cnt)
  (if (<= n 1)
    cnt
    (if (= (mod n 2) 0)
      (collatz_length2 (/ n 2) (1+ cnt))
      (collatz_length2 (+ (* n 3) 1) (1+ cnt)))))

(defun collatz_length (n) (collatz_length2 n 1))

【讨论】:

    猜你喜欢
    • 2011-02-15
    • 2019-05-18
    • 1970-01-01
    • 1970-01-01
    • 2010-10-20
    • 2021-05-26
    • 1970-01-01
    • 1970-01-01
    • 2011-11-26
    相关资源
    最近更新 更多