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

问题描述 投票:0回答:3

为什么我们必须使用

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
,因为它会被隐藏,但这几乎不是问题。

lisp common-lisp funcall
3个回答
31
投票

严格来说,

funcall
是不需要的,但有一些Lisp(Lisp-2变体,例如Common Lisp)将变量名称空间函数名称空间分开。 Lisp-1 变体(例如Scheme)没有做出这种区分。

更具体地说,在您的情况下,

test-func
位于变量名称空间中。

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

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

funcall

另请参阅此答案


24
投票

大多数 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

因此需要

funcall
apply
:

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

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


-2
投票

在 Common Lisp 中,每个符号都可以与其“符号函数”和“符号值”等相关联。读取列表时,默认情况下,Common Lisp 会解释: arg1 作为一个函数,因此检索 test-func

    symbol-function
  • ,这是未定义的——因此函数
    bar
    不起作用
    arg2 作为要 
    eval
    ed 的东西 - 因此函数
  • foo
  • 检索
    test-func
    symbol-value
    ,在你的情况下,它恰好是一个函数
    
        
    	
© www.soinside.com 2019 - 2024. All rights reserved.