(defun highest (lat)
(cond
((null lat) nil)
((null (cdr lat)) (car lat))
(T (higher (car lat) (highest (cdr lat))))))
(defun higher (a1 a2)
(cond
((> a1 a2) a1)
(T a2)))
此功能按预期工作:
> (highest '(3 5 1 2 3))
3. Trace: (HIGHEST '(3 5 1 2 3))
4. Trace: (HIGHEST '(5 1 2 3))
5. Trace: (HIGHEST '(1 2 3))
6. Trace: (HIGHEST '(2 3))
7. Trace: (HIGHEST '(3))
7. Trace: HIGHEST ==> 3
6. Trace: HIGHEST ==> 3
5. Trace: HIGHEST ==> 3
4. Trace: HIGHEST ==> 5
3. Trace: HIGHEST ==> 5
但是如果我将参数更改为&rest
:
(defun highest (&rest args)
(cond
((null args) nil)
((null (cdr args)) (car args))
(T (higher (car args) (highest (cdr args))))))
行为不一样。
> (highest 3 5 1 2 3)
3. Trace: (HIGHEST '3 '5 '1 '2 '3)
4. Trace: (HIGHEST '(5 1 2 3))
4. Trace: HIGHEST ==> (5 1 2 3)
*** - >: (5 1 2 3) is not a real number
编辑:对不起,我忘了提到我在第二种情况下传递了一个原子的参数。我编辑了这个问题以使其更加清晰。
答案 0 :(得分:5)
尝试在致电前评估(追踪最高)(最高3 2 10)。然后,您将看到第二个调用如下所示: (最高'(2 10)) 然后& rest参数看到一个碰巧是列表的对象。
要更正此问题,请使用APPLY。 APPLY就像funcall,但它的最后一个参数必须是一个列表,并被视为'拼接到'函数调用。像这样: (申请#'最高(cdr args))
答案 1 :(得分:3)
&rest
表单收集作为列表本身传递的所有其余参数。在highest (&rest args)
的情况下,args
实际上是一个包含元素,列表的列表。也就是说,在通话中,args
值为((3 2 10))
。
带highest
修饰符的&rest
的第二个版本始终进入条件的第二个测试,因为args
只是一个包含一个元素的列表(恰好是一个列表)。您返回args
的第一个元素,即列表本身(3 2 10)
。
两种结构的区别在于第一个版本,您将列表作为参数传递,该函数接收列表作为函数的唯一参数。在&rest
的情况下,所有参数(在这种情况下只是一个)被收集到一个列表中(在这种情况下只有一个元素)。
编辑:根据您的编辑,正如托马斯在评论中所述,使用&rest
暗示您必须使用apply
来递归调用。这是正确的实施:
(defun highest (&rest args)
(cond
((null args) nil)
((null (cdr args)) (car args))
(T (higher (car args) (apply #'highest (cdr args))))))
(请注意最后一行中的apply
)。