为什么funcall会忽略词法范围?

时间:2015-03-30 03:26:58

标签: lisp common-lisp

我被this old answer中的代码所激发,解释了为什么需要敏锐的引用,但我不明白为什么funcall似乎跳过了通常的范围规则。< / p>

(defun test () 'red)

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

我是否应该将Common Lisp理解为同时拥有&#34; local&#34;由let-family函数设置的词法范围符号绑定和由de-family函数设置的全局范围变量符号绑定?

3 个答案:

答案 0 :(得分:5)

假设使用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和LABELS

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

)      

答案 1 :(得分:3)

这实际上与funcall无关,而是quotefunction之间的差异。不使用funcall

再试一次
(defun test () 'red)

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

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

一旦你理解了这种差异,就应该相当清楚为什么funcall的行为和这个例子中的行为一样(至少,如果你理解funcall如何对符号进行操作 - 请参阅hyperspec entry)。 / p>

答案 2 :(得分:1)

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

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