在简单的 Lisp 代码中,它并没有太大的区别。动态绑定可能更符合早期 Lisp 方言的精神。词法绑定更符合函数式编程的精神,我们经常将高阶函数与闭包结合使用。
有一些情况支持词法绑定。
让我们看看动态绑定和特殊变量。
自由变量在运行时获得它们的绑定
Sylvester 在他的回答中描述了一个问题:自由变量可以通过动态绑定在运行时获取它们的值。
CL-USER 27 > (defun foo (x)
(declare (special x y))
(+ x y))
FOO
CL-USER 28 > (let ((y 10)) (declare (special y)) (foo 32))
42
CL-USER 29 > (foo 32)
以上可能导致只能在运行时调试的情况。从源头很难重建哪些值y
将具有,因为绑定可以发生在调用链中的任意位置。Richard Stallman 喜欢这个功能——他认为这是一个编辑器扩展语言的不错选择。Common Lisp 在很多事情上都使用了特殊的变量,例如在它的 I/O 系统中。例如流不需要一直通过参数传递。
自由变量在运行时可能无法获得它们的绑定
CL-USER 29 > (foo 32)
Error: The variable Y is unbound.
如果我们不为自由特殊变量定义顶级默认值,我们就不能只调用函数......
不是闭包
CL-USER 31 > (let ((x 10)) (declare (special x))
(lambda (y) (declare (special y x)) (+ y x)))
#<anonymous interpreted function 40600011F4>
不幸的是,这不是关闭。
请注意,变量*
保存最后一个结果,这里是上面的函数。
CL-USER 32 > (let ((x 22)) (declare (special x)) (mapcar * '(1 2 3)))
(23 24 25)
该函数使用动态绑定。
CL-USER 33 > (let ((x 10)) (declare (special x)) (lambda (y) (declare (special y x)) (+ y x)))
#<anonymous interpreted function 406000C304>
CL-USER 34 > (mapcar * '(1 2 3))
Error: The variable X is unbound.
同样,这不是一个闭包,我们不能只是传递它......
概括
我们真的想要一种语言,我们可以默认使用闭包,编译器可以警告未绑定的变量,并且大多数绑定很容易在源代码中发现。