为什么 Common Lisp 的 apply 函数会给出不同的结果?

Why does the Common Lisp's apply function give a different result?

当我在 Emacs SLIME 上尝试此代码时,apply 函数给出了不同的结果。它不应该给出相同的结果吗?为什么会给出不同的结果?谢谢

CL-USER> (apply #'(lambda (n)
             (cons n '(b a))) '(c)) 

(C B A)

CL-USER> (cons '(c) '(b a)) 

((C) B A)

cons 接受一个 element 和一个 list 作为参数。所以 (cons 'x '(a b c d)) 将 return (x a b c d).

apply 接受一个函数和一个 参数列表 -- 但参数将 不会 传递给函数作为清单!他们将被拆分并单独通过:

(apply #'+ '(1 2 3))

6

(实际上,它有一个函数,几个参数,其中 最后一个 必须是一个列表——这个列表将被拆分并视为 "the rest of the arguments to the function"。例如,尝试 (apply #'+ 5 1 '(1 2 3)),这将 return 12)

现在输入您的代码:

您传递给 apply 函数的最后一个参数是 '(c),一个包含一个元素 clist。 Apply 会将其视为参数列表,因此您传递给 lambda 表单的 第一个参数 c.

在第二次调用中,您将 '(c) 作为第一个参数传递给 cons。这是一个 list,它被正确地包含在结果列表的第一位:( (c) b a).

第二个调用将等同于第一个调用

(cons 'c '(b a))

(c b a)

如果您这样做,第一个调用将等同于第二个

(apply #'(lambda (n) (cons n '(b a))) '((c)))

((c) b a)
CL-USER 51 > (cons '(c) '(b a))
((C) B A)

CL-USER 52 > (apply #'(lambda (n)
                        (cons n '(b a)))
                    '(c))
(C B A)

让我们使用 FUNCALL:

CL-USER 53 > (funcall #'(lambda (n)
                          (cons n '(b a)))
                      '(c))
((C) B A)

另请参阅我们应用二元素列表时会发生什么:

CL-USER 54 > (apply #'(lambda (n)
                        (cons n '(b a)))
                    '(c d))

Error: #<anonymous interpreted function 40600008E4> got 2 args, wanted 1.

函数中的 &rest 参数与 apply.

之间存在对称性
(defun function-with-rest (arg1 &rest argn)
  (list arg1 argn))

(function-with-rest 1)         ; ==> (1 ())
(function-with-rest 1 2)       ; ==> (1 (2))
(function-with-rest 1 2 3 4 5) ; ==> (1 (2 3 4 5))

想象一下,我们想要采用 arg1argn,并以与 function-with-rest 相同的方式将其与我们选择的函数一起使用。我们将第一个参数加倍并对其余参数求和。

(defun double-first-and-sum (arg1 &rest argn)
  (apply #'+ (* arg1 2) argn)) 

(double-first-and-sum 1 1)     ; ==> 3
(double-first-and-sum 4 5 6 7) ; ==> 26

函数和 "rest" 个参数列表之间的参数是附加参数,它们总是在前面:

(apply #'+ 1 '(2 3 4)) ; ==> (+ 1 2 3 4)
(apply #'+ 1 2 3 '(4)) ; ==> (+ 1 2 3 4)

这非常方便,因为我们经常想添加比传递的参数更多的参数(否则我们可以使用 apply 首先使用的函数。这里有一个叫做 zip:

(defun zip (&rest args)
  (apply #'mapcar #'list args)) 

那么当你这样称呼它时会发生什么:(zip '(a b c) '(1 2 3))?那么 args 将是 ((a b c) (1 2 3))apply 将使其变为 (mapcar #'list '(a b c) '(1 2 3)),这将导致 ((a 1) (b 2) (c 3))。看到对称了吗?

因此,在您的示例中,您可以这样做:

(apply #'(lambda (&rest n)
            (cons n '(b a))) '(c)) 
;==> ((c) b a)

(apply #'(lambda (&rest n)
            (cons n '(b a))) '(c d e)) 
;==> ((c d e) b a)