我是一名新的lisp程序员,我正在尝试创建我的第一个递归lisp函数来返回两个列表中较大的一个。每次我运行该功能时,它似乎都会崩溃,因为我猜我的基本情况并未得到满足。这是我的代码。
(defun longest (l1 l2)
(cond ((null l1) (l2))
((null l2) (l1))
(t (rest l1)
(rest l2)
(longest l1 l2))
)
)
我知道当这两个列表的长度相同时,这种情况不会起作用,但我只想先得到一些工作。我知道这也可以通过while循环实现,但为了学习的目的,我想使用递归。
为什么这段代码永远不会终止?
答案 0 :(得分:4)
此代码永远不会终止,因为您一次又一次地使用相同的输入数据调用longest
。 rest
返回列表中除第一个元素之外的所有元素,而不是修改最初作为参数传递的列表。而且你还有其他错误:
(defun longest (l1 l2)
(cond ((null l1) (l2)) ;; <= will give error,
((null l2) (l1)) ;; because there is no such function like l2
(t (rest l1) ;; <= result ignored
(rest l2) ;; <= result ignored
(longest l1 l2)))) ;; <= called with the same l1 and l2
显然,它会一直运行,直到你的堆栈用完为止。预期行为的正确代码将是:
(defun longest (l1 l2)
(cond ((null l1) l2)
((null l2) l1)
(t
(longest (rest l1) (rest l2)))))
请注意,您可能无法获得此方法的预期,因为longest
每次都会获取列表的CDR,因此它只会返回最长列表的其余元素,所以如果你打电话给(longest '(1 2 3) '(1 2))
,你只会'(3)
,那可能不是你所期待的。
如果你想要它真的返回所有最长的列表,你必须修改你的代码。
答案 1 :(得分:2)
递归的一个有用技巧是构建一个查找关键信息的本地“worker”函数。 工人的结果比适当的“处理”得到你需要的最终答案。这类似于在命令式语言中经常使用迭代的方式,但更加灵活。
例如,在您的示例中,解决此问题的一种好方法是:
(defun longest (l1 l2)
(labels ((first-longer (a b)
(cond ((null a) nil)
((null b) t)
(t (first-longer (rest a) (rest b))))))
(if (first-longer l1 l2) l1 l2)))
使用示例:
(longest '(5 6 7 8) '(1 2 3)) --> (5 6 7 8)
(longest '(5 6 7) '(1 2 3 4)) --> (1 2 3 4)
(longest '(5 6 7) '(1 2 3)) --> (1 2 3)
请注意,应该如何处理等长列表存在歧义:最后一个示例可能已经返回(5 6 7)
答案 2 :(得分:1)
在您的代码中,当没有任何基本案例命中时,您可以在不改变任何参数的情况下自行调用。结果是默认情况将始终在所有后续迭代中出现。你似乎认为rest
以某种方式改变了它的论点,但它并没有。它返回结果而不更改原始参数。 (rest '(1 2 3)) ; ==> (2 3)
但(1 2 3)
仍然是(1 2 3)
而不是(2 3)
。如果您使用(rest x)
x
(1 2 3)
进行更改,则结果仍为(2 3)
,而x
仍为(1 2 3)
。
以下是我的看法:
;; return the longest of two lists
(defun longest (l1 l2)
(labels ((aux (tl1 tl2)
(cond ((null tl2) l1)
((null tl1) l2)
(t (aux (rest tl1) (rest tl2))))))
(aux l1 l2)))
如果它们的大小相同,你总会得到第一个参数。它与稳定排序相同,其中所有元素具有相同的权重,您将始终获得与您已有的相同的第一个值。另一种方法是,当它们长度相等时返回nil
,因为没有一个是最长的。
简单性通常要好得多。以下是我将如何实现它:
(defun longest (l1 l2)
(if (< (length l1)
(length l2))
l2
l1))