现在的位置: 首页 > 综合 > 正文

Lisp函数apply和funcall的比较

2013年06月06日 ⁄ 综合 ⁄ 共 8902字 ⁄ 字号 评论关闭


 CommonLisp提供了两个函数来通过函数对象来调用函数:Apply和Funcall函数。 得到函数对象的方法是#‘,其实就是FUNCTION函数。
例如: (foo  1 2 3) == (funcall #'foo  1 2 3)
apply和funcall第一个参数都是一个函数对象。这个对象或者是函数本身,例如#’+, 或者是一个符号,这个符号有一个函数在它的函数区域,例如 ‘+ 。
相同:对于apply和funcall 功能都是使用提供的的参数调用fn。
不同:但是参数的使用方法是不同的。 对于funcall,在函数描述符之后的其他所有的参数都是平等的,它们组合成一个列表让函数调用。
而在apply中最后一个参数必须为列表,并且是将列表中的每一个元素和其他元素作为一个平等的参数。它的参数是一个spreadable argument list designator 。
(spreadable argument list designator n. a designator for a list of objects; that is, an object that denotes a list and that is a non-null list L1 of length n, whose last element is a list L2 of length m (denoting a list L3 of length m+n-1 whose elements are
L1i for i < n-1 followed by L2j for j < m). ``The list (1 2 (3 4 5)) is a spreadable argument list designator for the list (1 2 3 4 5).'')

例如:
(funcall '+ 1 2 3)
(apply   '+ '(1 2 3))
(apply   '+ 1 2 '(3 4))
(apply   '+ 1 2 3 '())
(funcall '+ 1 2 3)

CL-USER> (apply #'list 1 nil)
(1)
CL-USER> (funcall #'list 1 nil)
(1 NIL)
如果Apply的最后一个参数是一个list的话,那么这个list将和前面的参数和在一起然后拆分为单独的参数让fn调用。Symbol nil等价于empty list. 所以第一个函数自用一个参数 整数1让函数list调用。
CL-USER> (apply #'list 1)
; Evaluation aborted on #<TYPE-ERROR expected-type: LIST datum: 1>.
CL-USER> (apply #'list 1 nil)    ;; nil == '(), produces args: '(1)
(1)
CL-USER> (apply #'list 1 '(nil))   ;; Ok, produces args: '(1 nil)
(1 NIL)

使用原则和惯例:
Apply一般在如果参数列表存储在一个list中或者参数个数不确定。例如:
(defun avg-slow (numbers)
  (/ (apply #'+ numbers)
     (1+ (length numbers))))

(shadow 'funcall) ; we'll define our own funcall.
;; Same thing means:
    (defun funcall (fun &rest args) (apply fun args))
你可以实现像上面那样实现funcall通过apply,但是你不能通过funcall实现apply,apply是更低级的原语:
(shadow 'apply)
    (defun apply (fun &rest args-and-arglist)
       (let ((args (append (butlast args-and-arglist)
                           (car (last args-and-arglist)))))
         (cl:apply 'funcall args)))
         ;; You need apply to implement apply ;-)

What is the difference between FUNCALL and APPLY?
FUNCALL is useful when the programmer knows the length of the argument list, but the function to call is either computed or provided as a parameter.  For instance, a simple implementation of MEMBER-IF (with none of the fancy options) could be written as:

(defun member-if (predicate list)
  (do ((tail list (cdr tail)))
      ((null tail))
   (when (funcall predicate (car tail))
     (return-from member-if tail))))

The programmer is invoking a caller-supplied function with a known
argument list.

APPLY is needed when the argument list itself is supplied or computed.
Its last argument must be a list, and the elements of this list become
individual arguments to the function.  This frequently occurs when a
function takes keyword options that will be passed on to some other
function, perhaps with application-specific defaults inserted.  For
instance:

(defun open-for-output (pathname &rest open-options)
  (apply #'open pathname :direction :output open-options))

FUNCALL could actually have been defined using APPLY:

(defun funcall (function &rest arguments)
  (apply function arguments))

文档:
Function APPLY
Syntax:
apply function &rest args+ => result*
Arguments and Values:
function---a function designator.
args---a spreadable argument list designator.
(spreadable argument list designator n. a designator for a list of objects; that is, an object that denotes a list and that is a non-null list L1 of length n, whose last element is a list L2 of length m (denoting a list L3 of length m+n-1 whose elements are
L1i for i < n-1 followed by L2j for j < m). ``The list (1 2 (3 4 5)) is a spreadable argument list designator for the list (1 2 3 4 5).'')
results---the values returned by function.
Description:
Applies the function to the args.
When the function receives its arguments via &rest, it is permissible (but not required) for theimplementation to bind the rest parameter to an object that shares structure with the last argument to apply. Because a function can neither detect whether it was
called via apply nor whether (if so) the last argument to apply was a constant, conforming programs must neither rely on the list structure of a rest list to be freshly consed, nor modify that list structure.
setf can be used with apply in certain circumstances; see Section 5.1.2.5 (APPLY Forms as Places).
Examples:
(setq f '+) =>  +
(apply f '(1 2)) =>  3
(setq f #'-) =>  #<FUNCTION ->
(apply f '(1 2)) =>  -1
(apply #'max 3 5 '(2 7 3)) =>  7
(apply 'cons '((+ 2 3) 4)) =>  ((+ 2 3) . 4)
(apply #'+ '()) =>  0

(defparameter *some-list* '(a b c))
(defun strange-test (&rest x) (eq x *some-list*))
(apply #'strange-test *some-list*) =>  implementation-dependent

(defun bad-boy (&rest x) (rplacd x 'y))
(bad-boy 'a 'b 'c) has undefined consequences.
(apply #'bad-boy *some-list*) has undefined consequences.
(defun foo (size &rest keys &key double &allow-other-keys)
   (let ((v (apply #'make-array size :allow-other-keys t keys)))
     (if double (concatenate (type-of v) v v) v)))
(foo 4 :initial-contents '(a b c d) :double t)
    =>  #(A B C D A B C D)

Function FUNCALL
Syntax:
funcall function &rest args => result*
Arguments and Values:
function---a function designator.
args---arguments to the function.
results---the values returned by the function.
Description:
funcall applies function to args. If function is a symbol, it is coerced to a function as if by finding itsfunctional value in the global environment.
Examples:
(funcall #'+ 1 2 3) =>  6
(funcall 'car '(1 2 3)) =>  1
(funcall 'position 1 '(1 2 3 2 1) :start 1) =>  4
(cons 1 2) =>  (1 . 2)
(flet ((cons (x y) `(kons ,x ,y)))
   (let ((cons (symbol-function '+)))
     (funcall #'cons
              (funcall 'cons 1 2)
              (funcall cons 1 2))))
=>  (KONS (1 . 2) 3)
Exceptional Situations:
An error of type undefined-function should be signaled if function is a symbol that does not have a global definition as a function or that has a global definition as a macro or a special operator.
Notes:
(funcall function arg1 arg2 ...)
==  (apply function arg1 arg2 ... nil)
==  (apply function (list arg1 arg2 ...))
The difference between funcall and an ordinary function call is that in the former case the function is obtained by ordinary evaluation of a form, and in the latter case it is obtained by the special interpretation of the function position that normally occurs.

Funcall, Apply, and Mapcar
Earlier I promised to give some functions which take functions as arguments. Here they are:
> (funcall #'+ 3 4)
7
> (apply #'+ 3 4 '(3 4))
14
> (mapcar #'not '(t nil t nil t nil))
(NIL T NIL T NIL T)
Funcall calls its first argument on its remaining arguments.
Apply is just like funcall, except that its final argument should be a list; the elements of that list are treated as if they were additional arguments to a funcall.
The first argument to mapcar must be a function of one argument; mapcar applies this function to each element of a list and collects the results in another list.
Funcall and apply are chiefly useful when their first argument is a variable. For instance, a search engine could take a heuristic function as a parameter and use funcall or apply to call that function on a state description. The sorting functions described
later use funcall to call their comparison functions.

you should use funcall if you have one or more separate arguments and apply if you have your arguments in a list
(defun passargs (&rest args) (apply #'myfun args))
or
(defun passargs (a b) (funcall #'myfun a b))

Mapcar, along with nameless functions (see below), can replace many loops.

英文:
? (apply #'list 1 nil)
(1)
? (funcall #'list 1 nil)
(1 NIL)
Note that the argument to FUNCALL (and APPLY) can either be the function itself, i.e. #'+, or a symbol which has the function in its function cell (is fbound to the function), i.e. '+.

If the last argument of APPLY is a list its elements are spread as separate arguments to the function. The symbol NIL is the same thing as the empty list (). So, in the first example there is only one argument (integer 1) for function LIST.
Both funcall and apply invoke a function with the supplied arguments. However, the way the arguments are given by the user is different. For funcall, anything after the "function designator" is considered an argument and (conceptually) collected into the argument
list to the function to be called.
For apply, you have to provide arguments such that the last argument is a list. Before invoking the desired function, apply will then (conceptually) produce a single list containing all the arguments and use it as argument list for the specified function.

Hence,
compare

CL-USER> (apply #'list 1)
; Evaluation aborted on #<TYPE-ERROR expected-type: LIST datum: 1>.

CL-USER> (apply #'list 1 nil)    ;; nil == '(), produces args: '(1)
(1)

CL-USER> (apply #'list 1 '(nil))   ;; Ok, produces args: '(1 nil)
(1 NIL)

The behavior of apply is especially well suited if the arguments to a function are stored in a list anyway or if their number is not fixed.

I.e.

(defun avg-slow (numbers)
  (/ (apply #'+ numbers)
     (1+ (length numbers))))
(shadow 'funcall) ; we'll define our own funcall.

    ;; Same thing means:
    (defun funcall (fun &rest args) (apply fun args))

The only difference is that funcall doesn't take a list of arguments,
but just plain arguments.

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

Now, actually apply also take plain arguments, before the list of
arguments:

   (apply   '+ 1 2 '(3 4))
   (apply   '+ 1 2 3 '())
   (funcall '+ 1 2 3)

Finally, you can implement funcall in terms of apply as above, but you
cannot implement apply in terms of funcall: apply is more primitive.

    (shadow 'apply)
    (defun apply (fun &rest args-and-arglist)
       (let ((args (append (butlast args-and-arglist)
                           (car (last args-and-arglist)))))
         (cl:apply 'funcall args)))
         ;; You need apply to implement apply ;-)

Why need funcall or apply?
◊ Without funcall or apply the function would look like the
following.
» (defun sumInt (func n)
(cond ((equal n 0) 0)
( t ( + (func n)
(sumInt ‘func (1- n))))
))
◊ Why does the above not work?
» In the expression (func n) the symbol-function of func is used to find the function to apply.
» But because func is an argument the definition is in func's symbol-value

参考:
Common Lisp Hyperspec.

APPLY:

http://www.lispworks.com/documentation/HyperSpec/Body/f_apply.htm#apply

FUNCALL:

http://www.lispworks.com/documentation/HyperSpec/Body/f_funcal.htm#funcall

http://cl-cookbook.sourceforge.net/functions.html

新闻组:

https://groups.google.com/forum/?fromgroups#!topic/comp.lang.lisp/l6RGIT1WPWM

抱歉!评论已关闭.