【问题标题】:Why does funcall ignore the lexical scope?为什么 funcall 忽略词法范围?
【发布时间】:2015-03-30 03:26:57
【问题描述】:

this old answer 中的代码解释了为什么需要尖引号,但我不明白为什么 funcall 似乎跳过了通常的范围规则。

(defun test () 'red)

(flet ((test () 'green))
  (list (funcall 'test)
        (funcall #'test))) => (red green)

我是否应该将 Common Lisp 理解为同时具有由 let-family 函数设置的“本地”词法范围符号绑定和由 de-family 函数设置的全局范围变量符号绑定?

【问题讨论】:

  • 这不是关于funcall,而是关于#'function 特殊运算符。
  • 对我来说,这似乎是关于quote

标签: lisp common-lisp


【解决方案1】:

假设是 Common Lisp。

DEFUN 和朋友们

DEFUN 创建一个全局 函数绑定,可以通过符号检索。

(defun foo () 'foo)

上面我们有一个函数 FOO。

我们称之为:

(funcall (function foo))   ; no lexical bound function available, so it uses
                           ; the symbol's binding

(funcall (symbol-function 'foo))

(funcall 'foo)

(foo)

以上都访问相同的功能。

注意:上面显示(foo)(funcall 'foo) 调用相同的函数。有一个例外:文件编译器可能假定函数FOO 不会改变。这允许 Lisp 编译器内联代码或编译为更快的函数调用代码。通过 (funcall 'foo) 中的符号调用函数始终会导致调用当前和最新的绑定 - 因此始终需要通过符号进行查找。

FLET 和标签

FLET 和 LABELS 创建词法范围的函数绑定。 FUNCTION 可以引用这样的绑定。请注意,这些绑定不能在运行时通过符号访问。只有两种方式:

  • 调用函数(foo)

  • 通过(function foo)引用函数。

由于两者都使用静态词法引用,因此在运行时不会通过符号或类似方式进行查找。这意味着,符号在运行时不涉及词法函数——它们只在源代码中可见。

(flet ((foo () 'bar))   ; <- local lexical scope, function binding

   (foo)                          ; calls the lexical bound function foo

  (funcall (function foo))        ; calls the lexical bound function foo

但是

  (funcall (symbol-function 'foo))   ; calls the symbol's binding,
                                     ; not the lexical binding

  (funcall 'foo)                     ; calls the symbol's binding
                                     ; not the lexical binding

)      

【讨论】:

    【解决方案2】:

    这实际上与funcall 没有太大关系,而是quotefunction 之间的区别。在没有funcall 的情况下再试一次:

    (defun test () 'red)
    
    (flet ((test () 'green))
      (list 'test #'test)) => (TEST #<FUNCTION (FLET TEST) {C14D26D}>)
    

    其中一个是符号,另一个是函数对象—— test 的(词法绑定)函数值。如您所见,quote 返回它的参数(不计算它)——这就是词法范围被忽略的地方。

    一旦您了解了这种差异,就应该很清楚为什么funcall 的行为与本示例中的行为一样(至少,如果您了解 funcall 如何对符号进行操作 - 请参阅hyperspec entry)。

    【讨论】:

      【解决方案3】:

      funcall 函数采用函数指示符(函数对象或符号)。使用#'foo,您可以检索绑定到foo 的函数对象,在词法上下文中对 this 进行评估。使用'foo,您可以创建符号 foo。

      funcall 然后将函数指示符映射到函数时,它要么只是标识(如果您传递函数对象,则使用 this)或必须在全局环境中查找。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-07-06
        • 1970-01-01
        • 1970-01-01
        • 2014-07-13
        • 2017-10-20
        • 2019-12-07
        • 1970-01-01
        相关资源
        最近更新 更多