为什么lambda返回一个全局变量而不是局部变量?

时间:2016-06-06 22:18:23

标签: common-lisp

测试#1

我有一个全局声明的变量f,以及一个名为f的参数的函数:

(defvar f 1)

(defun test (f)
    f)

我调用该函数并返回参数的值:

(test 2)
=> 2

测试#2

我再次拥有一个全局声明的变量f,以及一个名为f的参数的函数。但是,这一次,函数返回lambda,返回f

(defvar f 1)

(defun test (f)
     #'(lambda ()
         f))

我调用该函数并返回lambda函数。然后我调用lambda函数,它返回全局f

的值
(funcall (test 2))
=> 1

我很惊讶。我认为lambda函数是一个闭包,它将返回本地f,而不是全局f。如何修改test函数和/或lambda函数,以便lambda函数返回本地f,而不是全局f

指向讨论此特定范围问题的在线资源的指针将不胜感激。

1 个答案:

答案 0 :(得分:9)

通过使用defvar,您声明f一个特殊的(也就是动态绑定的)变量。您的代码中的f不再在词法上被关闭,但实际上与全局变量暂时更改为2相同。

由于此功能,lispers对没有*earmuffs*的全局变量感到不满意。一旦他们*earmuffs*,就会更容易看到它:

(defvar *f* 1)                       ; special variable *f*
(defun test (*f*)                    ; momentarily rebind *f*
     (format nil "*f* is ~a~%" *f*)  ; use new value
     #'(lambda ()                    ; return lambda using *f*
         *f*))                       ; *f* goes back to being 1
(funcall (test 2))                   ; ==> 1 (prints "*f* is 2\n")

所以教训是:永远不要在没有*earmuffs*的情况下创建全局变量,因为你会得到几乎无法检测到的疯狂的运行时错误。这个命名惯例不仅仅适合时尚!

至于文档,hyperspec实际上显示了defparameter and defvar的示例中动态变量的工作原理。看到他们在调用(foo) => (P V)期间调用foo并且*p*重新绑定*v*bar,并且由于它们是动态绑定的,{{1}使用更改的值。