为什么 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)
,一个包含一个元素 c
的 list。 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))
想象一下,我们想要采用 arg1
和 argn
,并以与 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)
当我在 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)
,一个包含一个元素 c
的 list。 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))
想象一下,我们想要采用 arg1
和 argn
,并以与 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)