【问题标题】:Why do we need funcall in Lisp?为什么我们在 Lisp 中需要 funcall?
【发布时间】:2012-04-01 12:58:28
【问题描述】:

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

【问题讨论】:

  • “几乎没有问题”是问题的症结所在。在实践中,我发现调用 funcall 几乎没有问题,如果我必须为变量选择有创意/怪异的名称以避免与 CAR、LIST、REST 等冲突,则会带来不便。

标签: lisp common-lisp funcall


【解决方案1】:

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

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

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

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

另见this answer

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 是的,不遮蔽函数的优势很好,但另一方面,我们得到了函数的特殊情况。我假设因为我们不必担心 lisp 中的括号定位,我们需要其他宗教战争 ;) 既然你和 Diego 都很好地回答了我的问题,我作为决胜局的人随时间去接受了 diego 的回答。
    【解决方案3】:

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

    • arg1 作为函数检索test-funcsymbol-function,这是未定义的——因此函数bar 不起作用
    • arg2 是 evaled - 因此函数 foo 检索 test-funcsymbol-value,在您的情况下,它恰好是一个函数

    【讨论】:

    • 这不是 Common Lisp 实现所做的。您可能想了解词法绑定。
    猜你喜欢
    • 2019-06-09
    • 2014-06-18
    • 2017-02-26
    • 2011-04-03
    • 2017-07-27
    • 2020-09-21
    • 2020-03-09
    • 2018-12-24
    • 2012-04-08
    相关资源
    最近更新 更多