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

Rog*_*llo 2 common-lisp

测试#1

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

(defvar f 1)

(defun test (f)
    f)
Run Code Online (Sandbox Code Playgroud)

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

(test 2)
=> 2
Run Code Online (Sandbox Code Playgroud)

测试#2

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

(defvar f 1)

(defun test (f)
     #'(lambda ()
         f))
Run Code Online (Sandbox Code Playgroud)

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

(funcall (test 2))
=> 1
Run Code Online (Sandbox Code Playgroud)

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

将讨论指向讨论此特定范围问题的在线资源的指针.

Syl*_*ter 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")
Run Code Online (Sandbox Code Playgroud)

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

至于文档,hyperspec实际上显示了动态变量在示例中如何工作defparameterdefvar.看到它们调用(foo) => (P V)foo重新绑定*p**v*在调用期间bar,因为它们是动态绑定的,所以bar使用更改的值.