问题描述
练习 8.24 和 8.26 David Touretzky 的“符号计算的温和介绍”状态:
8.24: 编写 COUNT-DOWN,一个使用 list-consing 递归从 n 开始倒数的函数。 (计数 5)应生成列表 (5 4 3 2 1)。
8.26:假设我们想修改 COUNT-DOWN 以便列出它 构造以零结尾。例如,(COUNT-DOWN 5) 将产生 (5 4 3 2 1 0)。 [...]
所以我尝试将这两个练习组合成一个函数 countdown
,它接受一个关键字参数 incl-zero
,如下所示:
(defun countdown (n &key (incl-zero nil))
(cond
((zerop n) (if incl-zero '(0) nil))
(t (cons n (countdown (1- n))))))
(countdown 5)
(countdown 5 :incl-zero t)
然而,两个对倒计时的调用都返回 (5 4 3 2 1)
,所以 :incl-zero t
似乎没有达到 if 条件。为什么会这样?
解决方法
我觉得自己很傻。
(defun countdown (n &key (incl-zero nil))
(cond
((zerop n) (if incl-zero '(0) nil))
(t (cons n (countdown (1- n) :incl-zero incl-zero)))))
(countdown 5)
(countdown 5 :incl-zero t)
我猜是递归模因的牺牲品..
,您注意到了这个错误,但请注意,当您的参数从一次调用到另一次调用时不会改变,您可能应该定义一个局部递归函数来仅传递确实改变的参数。这更易于编写和理解。此外,传递关键字参数可能会带来一些运行时开销。
(defun countdown (n &key (incl-zero nil))
(labels ((recurse (n)
(cond
((zerop n) (if incl-zero '(0) nil))
(t (cons n (recurse (1- n)))))))
(recurse n)))