可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Why do we have to use funcall
to call higher order functions in Common Lisp? For example, why do we have to use:
(defun foo (test-func args)
(funcall test-func args))
instead of the simpler:
(defun bar (test-func args)
(test-func args))
Coming from a procedural background, I'm a bit surprised by that since the languages I'm more used to (e.g. Python, C#) don't need the distinction. In particular, on the source level at least, the C# compiler transforms it to something like func.invoke()
.
The only problem I see is that this would mean we couldn't call a global function test-func
anymore because it'd be shadowed, but that's hardly a problem.
回答1:
Strictly speaking, funcall
would not be needed, but there are some lisps (list-2 implementations, such as Common Lisp) that separate the variable name space of the function name space. List-1 implementations (e.g. Scheme) do not make this distinction.
More specifically, in your case, test-func
is in the variable name space.
(defun foo (test-func args)
(funcall test-func args))
Therefore you need a construct that actually searches the function object associated with this variable in the variable name space. In Common Lisp this construct is funcall
.
See also this answer.
回答2:
The majority of Lisps have two namespaces (functions and variables). A name is looked up in the function namespace when it appears as the first element in an S-expression, and in the variable namespace otherwise. This allows you to name your variables without worrying about whether they shadow functions: so you can name your variable list
instead of having to mangle it into lst
.
However, this means that when you store a function in a variable, you can't call it normally:
(setq list #'+) ; updates list in the variable namespace
(list 1 2 3) => (1 2 3) ; looks up list in the function namespace
Hence the need for funcall
and apply
:
(funcall list 1 2 3) => 6 ; looks up list in the variable namespace
(Not all Lisps have two namespaces: Scheme is an example of a Lisp with just one namespace.)
回答3:
Note that in any Lisp, if you want to call a function in some way other than
(function fixed arg u ments)
you have to use something in the first position of the form other than the function anyway. In some dialects, if F
is a variable which holds a function, you can just do this:
(f a b c) ;; no funcall
But in pretty much all dialects, you can't remove the apply
(apply f args-list) ;; Common Lisp or Scheme: same
If you're running into funcall
being a nuisance, you are just not using enough interesting applicators over your functional arguments. :)
回答4:
In Common Lisp, each symbol can be associated with its symbol-function and its symbol-value, among other things. When reading a list, by default, Common Lisp interprets:
- arg1 as a function and so retrieves
test-func
's symbol-function
, which is undefined -- thus function bar
doesn't work
- arg2 as something to be
eval
ed -- thus function foo
retrieves test-func
's symbol-value
, which, in your case, happens to be a function