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

Voo*_*Voo 18 lisp common-lisp funcall

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

(defun foo (test-func args)
  (funcall test-func args))
Run Code Online (Sandbox Code Playgroud)

而不是更简单:

(defun bar (test-func args)
  (test-func args))
Run Code Online (Sandbox Code Playgroud)

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

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

Die*_*lla 19

严格来说,funcall就没有必要了,但也有一些的Lisp(名单-2实现,如Common Lisp的)该独立的变量名称空间中的函数名称空间.List-1实现(例如Scheme)没有做出这种区分.

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

(defun foo (test-func args)
  (funcall test-func args))
Run Code Online (Sandbox Code Playgroud)

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

另见这个答案.


Gar*_*ees 16

大多数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
Run Code Online (Sandbox Code Playgroud)

因此需要funcallapply:

(funcall list 1 2 3) => 6 ; looks up list in the variable namespace
Run Code Online (Sandbox Code Playgroud)

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