为什么我们需要在Lisp中使用funcall?

时间:2012-03-15 23:07:44

标签: lisp common-lisp funcall

为什么我们必须使用funcall来调用Common 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,因为它会被遮蔽,但这几乎不是问题。

3 个答案:

答案 0 :(得分:19)

严格地说,不需要funcall,但是有一些lisps(list-2实现,例如Common Lisp)分隔变量名称空间功能名称空间。 List-1实现(例如Scheme)没有做出这种区分。

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

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

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

另见this answer

答案 1 :(得分:15)

大多数Lisps都有两个名称空间(函数和变量)。当函数名称空间显示为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

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

答案 2 :(得分:-2)

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

  • arg1作为一个函数,因此检索test-func的{​​{1}},这是未定义的 - 因此函数symbol-function不起作用
  • arg2作为bar编辑的内容 - 因此函数eval检索foo的{​​{1}},在您的情况下,它恰好是一个函数