24

为什么我们必须使用funcallCommon Lisp 中调用高阶函数?例如,为什么我们必须使用:

(defun foo (test-func args)
  (funcall test-func args))

而不是更简单的:

(defun bar (test-func args)
  (test-func args))

来自程序背景,我对此感到有点惊讶,因为我更习惯的语言(例如 Python、C#)不需要区分。特别是,至少在源代码级别,C# 编译器将其转换为类似func.invoke().

我看到的唯一问题是,这意味着我们不能再调用全局函数test-func,因为它会被遮蔽,但这几乎不是问题。

4

3 回答 3

23

严格来说,funcall是不需要的,但是有一些 lisp(list-2 实现,例如 Common Lisp)将函数名称空间的变量名称空间分开。List-1 实现(例如 Scheme)没有这种区别。

更具体地说,在您的情况下,test-func是在变量名称空间中。

(defun foo (test-func args)
  (funcall test-func args))

因此,您需要一个在变量名称空间中实际搜索与此变量关联的函数对象的构造。在 Common Lisp 中,这个结构是funcall.

另请参阅此答案

于 2012-03-15T23:32:04.737 回答
19

大多数 Lisp 有两个命名空间(函数和变量)。当名称作为 S 表达式中的第一个元素出现时,在函数名称空间中查找名称,否则在变量名称空间中查找。这使您可以命名变量而不必担心它们是否会影响函数:因此您可以命名变量list而不必将其分解为lst.

但是,这意味着当您将函数存储在变量中时,您无法正常调用它:

(setq list #'+) ; updates list in the variable namespace
(list 1 2 3) => (1 2 3) ; looks up list in the function namespace

因此需要funcallapply

(funcall list 1 2 3) => 6 ; looks up list in the variable namespace

(并不是所有的 Lisp 都有两个命名空间:Scheme 是一个只有一个命名空间的 Lisp 示例。)

于 2012-03-15T23:32:22.783 回答
-2

在 Common Lisp 中,每个符号都可以与其符号函数符号值相关联。读取列表时,默认情况下,Common Lisp 会解释:

  • arg1 作为一个函数,因此检索test-func's symbol-function,这是未定义的 - 因此函数bar不起作用
  • arg2 作为要eval编辑的东西 - 因此函数foo检索test-func's symbol-value,在你的情况下,它恰好是一个函数
于 2012-03-16T00:22:06.910 回答