Common Lisp中的dynamic和词汇variables
我正在阅读Peter Seibel的“Practical Common Lisp”一书。
在第6章“variables”部分“词汇variables和闭包”和“dynamic,又名特殊variables”中。 http://www.gigamonkeys.com/book/variables.html
我的问题是,这两个部分的例子显示了如何(让…)可以影响全局variables,并没有真正说明dynamic和词法变化之间的差异。
我明白闭包是如何工作的,但在这个例子中,我并没有真正了解这个特别的东西:
(defvar *x* 10) (defun foo () (format t "Before assignment~18tX: ~d~%" *x*) (setf *x* (+ 1 *x*)) (format t "After assignment~18tX: ~d~%" *x*)) (defun bar () (foo) (let ((*x* 20)) (foo)) (foo)) CL-USER> (foo) Before assignment X: 10 After assignment X: 11 NIL CL-USER> (bar) Before assignment X: 11 After assignment X: 12 Before assignment X: 20 After assignment X: 21 Before assignment X: 12 After assignment X: 13 NIL
我觉得这里没有什么特别的。 bar中的外部foo递增全局x , foo包围的foo增加shadowed x 。 有什么大不了的? 我不明白这是如何解释词法和dynamicvariables之间的区别的。 然而这本书继续如下:
那么这是如何工作的? LET如何知道,当它绑定x它应该创build一个dynamic绑定,而不是一个正常的词法绑定? 它知道,因为这个名字已经被声明为特殊的.12用DEFVAR和DEFPARAMETER定义的每个variables的名字都被自动地声明为全局特殊的。
如果让我们使用“正常词法绑定”绑定x,会发生什么? 总而言之,dynamic绑定和词法绑定有什么区别?这个例子在dynamic绑定方面有什么特别之处?
当一个variables在词汇范围内时 ,系统会查找函数定义的位置,以查找自由variables的值。 当一个variables是dynamic范围的 ,系统会查找函数被调用的位置以find自由variables的值。 Common Lisp中的variables默认都是词法, 但是,可以使用defvar或defparameter在顶层定义dynamic范围的variables。
一个更简单的例子
词汇范围(与setq):
(setq x 3) (defun foo () x) (let ((x 4)) (foo)) ; returns 3
dynamic范围(与defvar):
(defvar x 3) (defun foo () x) (let ((x 4)) (foo)) ; returns 4
如何知道一个variables是词法还是dynamic? 它不。 另一方面,当foo去findX的值时,它将首先find在顶层定义的词汇值。 然后检查variables是否应该是dynamic的。 如果是这样的话,那么foo会看到呼叫环境,在这种情况下,使用let让X的值为4。
(注意:这是一个过于简化,但它将帮助可视化不同的范围规则之间的区别)
这是怎么回事?
你说: 感觉就像这里没有什么特别的事情发生。 bar
的外部foo
递增全局x
, foo
包围的foo
增加shadowed x
。 有什么大不了的?
这里特别的是LET
可以影响*x*
的值。 用词法variables是不可能的。
代码通过DEFVAR
声明*x*
是特殊的。
在FOO
, *x*
的值是dynamic的。 FOO
将采用*x*
的当前dynamic绑定 ,或者如果没有,则采用符号*x*
的符号值。 例如,可以使用LET
引入新的dynamic绑定 。
另一方面词汇variables必须存在于词汇环境中。 LET
, LAMBDA
, DEFUN
等可以引入这样的词汇variables。 请看这里以三种不同的方式介绍的词汇variablesx
:
(let ((x 3)) (* (sin x) (cos x))) (lambda (x) (* (sin x) (cos x))) (defun baz (x) (* (sin x) (cos x)))
如果我们的代码是:
(defvar x 0) (let ((x 3)) (* (sin x) (cos x))) (lambda (x) (* (sin x) (cos x))) (defun baz (x) (* (sin x) (cos x)))
那么在上述所有三种情况下, X
都是特殊的 ,因为DEFVAR
声明声明了X
是特殊的 – 对所有级别都是全局的。 因此,将特殊variables声明为*X*
是惯例。 因此,只有周围有恒星的variables才是特殊的 – 按照惯例 。 这是一个有用的惯例。
在你的代码中,你有:
(defun bar () (foo) (let ((*x* 20)) (foo)) (foo))
由于*x*
已经通过代码中的DEFVAR
声明为特殊的,所以LET
构造为*x*
引入了新的dynamic绑定 。 然后FOO
被调用。 由于在FOO
, *x*
使用dynamic绑定 ,所以它查找当前的一个,发现*x*
被dynamic地绑定到20
。
在当前的dynamic绑定中find特殊variables的值。
本地特别声明
还有地方special
声明:
(defun foo-s () (declare (special *x*)) (+ *x* 1))
如果variables被DEFVAR
或DEFPARAMETER
声明为特殊的,则可以省略本地special
声明。
一个词法variables直接引用variables绑定:
(defun foo-l (x) (+ x 1))
让我们在实践中看到它:
(let ((f (let ((x 10)) (lambda () (setq x (+ x 1)))))) (print (funcall f)) ; form 1 (let ((x 20)) ; form 2 (print (funcall f))))
这里所有的variables都是词汇。 在表单2中 , LET
不会影响函数f
的X
它不能。 该函数使用由LET ((X 10)
引入的词法绑定variables,在forms2中用另一个词法绑定的X
围绕该调用对我们的函数没有影响。
让我们试试特殊的variables:
(let ((f (let ((x 10)) (declare (special x)) (lambda () (setq x (+ x 1)))))) (print (funcall f)) ; form 1 (let ((x 20)) ; form 2 (declare (special x)) (print (funcall f))))
现在怎么办? 那样有用吗?
它不是!
第一种forms调用函数,它试图查找X
的dynamic值,没有。 我们在表单1中得到一个错误: X
没有绑定,因为没有dynamic绑定的效果。
表格2将起作用,因为带有special
声明的LET
引入了对X
的dynamic绑定。
也许这个例子会有所帮助。
;; the lexical version (let ((x 10)) (defun lex-foo () (format t "Before assignment~18tX: ~d~%" x) (setf x (+ 1 x)) (format t "After assignment~18tX: ~d~%" x))) (defun lex-bar () (lex-foo) (let ((x 20)) ;; does not do anything (lex-foo)) (lex-foo)) ;; CL-USER> (lex-bar) ;; Before assignment X: 10 ;; After assignment X: 11 ;; Before assignment X: 11 ;; After assignment X: 12 ;; Before assignment X: 12 ;; After assignment X: 13 ;; the dynamic version (defvar *x* 10) (defun dyn-foo () (format t "Before assignment~18tX: ~d~%" *x*) (setf *x* (+ 1 *x*)) (format t "After assignment~18tX: ~d~%" *x*)) (defun dyn-bar() (dyn-foo) (let ((*x* 20)) (dyn-foo)) (dyn-foo)) ;; CL-USER> (dyn-bar) ;; Before assignment X: 10 ;; After assignment X: 11 ;; Before assignment X: 20 ;; After assignment X: 21 ;; Before assignment X: 11 ;; After assignment X: 12 ;; the special version (defun special-foo () (declare (special *y*)) (format t "Before assignment~18tX: ~d~%" *y*) (setf *y* (+ 1 *y*)) (format t "After assignment~18tX: ~d~%" *y*)) (defun special-bar () (let ((*y* 10)) (declare (special *y*)) (special-foo) (let ((*y* 20)) (declare (special *y*)) (special-foo)) (special-foo))) ;; CL-USER> (special-bar) ;; Before assignment X: 10 ;; After assignment X: 11 ;; Before assignment X: 20 ;; After assignment X: 21 ;; Before assignment X: 11 ;; After assignment X: 12
你也可以告诉你的Lispdynamic绑定局部variables:
(let ((dyn 5)) (declare (special dyn)) ... ;; DYN has dynamic scope for the duration of the body )
重写PCL的例子。
;;; Common Lisp is lexically scoped by default. λ (setq x 10) => 10 λ (defun foo () (setf x (1+ x))) => FOO λ (foo) => 11 λ (let ((x 20)) (foo)) => 12 λ (proclaim '(special x)) => NIL λ (let ((x 20)) (foo)) => 21
来自On Lisp的另一个很好的解释,第2.5章范围:
Common Lisp是一个词法范围的Lisp。 scheme是语言最古老的方言, 在Scheme之前,dynamic范围被认为是Lisp的一个重要特征。
词法和dynamic范围的区别归结为实现如何处理自由variables。 如果一个符号已经被build立为一个variables,或者通过作为参数出现,或者通过像let和do这样的variables绑定运算符,那么它就被绑定在一个expression式中。 没有绑定的符号被认为是免费的。 在这个例子中,范围起作用:
(let ((y 7)) (defun scope-test (x) (list xy)))
在defunexpression式中,x是绑定的,y是自由的。 自由variables是有趣的,因为它们的值应该是不明显的。 绑定variables的值没有不确定性 – 当调用scope-test时,x的值应该是作为parameter passing的值。 但是y的价值应该是什么? 这是方言范围规则所回答的问题。
在一个dynamic范围的Lisp中,为了在执行范围testing时find一个自由variables的值,我们回顾一下调用它的函数链。 当我们发现一个绑定了y的环境时,y的绑定就是scope-test中使用的绑定。 如果我们没有发现,我们取y的全球价值。 因此,在一个dynamic范围的Lisp中,y将具有它在调用expression式中的值:
> (let ((y 5)) (scope-test 3)) (3 5)
对于dynamic范围,当范围testing被定义时,意味着没有任何东西是必然的。 重要的是,当调用范围testing时,y的值为5。
在一个词法范围的Lisp中,我们不是通过调用函数链来回顾,而是在定义函数的时候回顾一下包含的环境。 在一个词法范围的Lisp中,我们的例子将捕获定义了scope-test的y的绑定。 所以这是Common Lisp会发生的事情:
> (let ((y 5)) (scope-test 3)) (3 7)
这里在调用时y到5的绑定对返回值没有影响。
尽pipe通过声明一个variables是特殊的,你仍然可以获得dynamic范围,但是词法范围是Common Lisp中的默认范围。 总的来说,Lisp社区似乎没有多less遗憾地看待dynamic范围的传递。 首先,它曾经导致可怕的难以捉摸的错误。 但是词汇范围不仅仅是一种避免错误的方法。 下一节将展示,这也使得一些新的编程技术成为可能。