测试#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
?
指向讨论此特定范围问题的在线资源的指针将不胜感激。
答案 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}使用更改的值。