在课堂上和LISP一起玩。这无疑是我写的第一个LISP代码。我无法弄清楚为什么此代码会为2000以上的函数"invocation stack history overflow"
生成错误(longest_collatz n)
。任何有这种语言经验的人都可以帮我理解错误吗?
(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)))
答案 0 :(得分:2)
Yout collatz
函数不是尾递归的,因此即使编译代码也不太可能将它转换为循环。
您可以使用累加器重写它,以便编译器将其转换为循环:
(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重新实现)。
注意:
elt
;相反,使用first
和second
必须更好。reduce
重写maxlist
会使其更快更清晰。答案 1 :(得分:0)
这是一个只返回collatz列表长度而不是列表本身的函数。它可能更有效(并且是尾递归)。
(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))