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

时间:2015-07-31 20:08:49

标签: common-lisp

When I try this code on Emacs SLIME, the apply function gives a different result. Isn't it supposed to give the same result? Why does it give a different result? Thanks.

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

(C B A)

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

((C) B A)

3 个答案:

答案 0 :(得分:6)

cons takes an element and a list as arguments. So (cons 'x '(a b c d)) will return (x a b c d).

apply takes a function and a list of arguments -- but the arguments will not be passed to the function as a list! They will be split and passed individually:

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

6

(actually, it takes one function, several arguments, of which the last must be a list -- this list will be split and treated as "the rest of the arguments to the function". try, for example, (apply #'+ 5 1 '(1 2 3)), which will return 12)

Now to your code:

The last argument you passed to the apply function is '(c), a list with one element, c. Apply will treat it as a list of arguments, so the first argument you passed to your lambda-form is c.

In the second call, you passed '(c) as first argument to cons. This is a list, which was correctly included in the first place of the resulting list: ( (c) b a).

The second call would be equivalent to the first if you did

(cons 'c '(b a))

(c b a)

And the first call would be equivalent to the second if you did

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

((c) b a)

答案 1 :(得分:2)

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

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

Let's use FUNCALL:

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

See also what happens when we apply a two element list:

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

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

答案 2 :(得分:2)

函数和apply中的&amp; rest参数之间存在对称性。

(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

函数和&#34; rest&#34;之间的参数。参数是总是第一个的附加参数:

(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) 。你看到了对称吗?

因此,您可以在您的示例中完成此操作:

{{1}}