旧的Lisp,包括Common Lisp在某些时候,以及在emacs 24.1之前的elisp,是动态范围的,在我读过的资源中,似乎共识似乎是词法范围更适合编程。
我感觉动态范围合法地更容易理解。动态范围可以完全根据符号及其值来考虑。词汇范围需要一个新的概念"绑定"据我所知,它破坏了Lisp语言的一些简洁/优雅。
我一直在思考如何提出这个问题,"词法范围是否打破了一些优雅?"更加严格,我最好的想法是看看它们是否相同,即是否可以实现另一个。
是否可以将词法范围的lisp实现为动态范围列表中的简单扩展?如果没有解释器引入新的ad-hoc符号,是否可以这样做?
更具体地说,创建词法作用域的let
可以在动态范围的lisp中实现吗?
答案 0 :(得分:4)
动态范围更容易实现,也许更容易理解,但它使代码更难以阅读:
(setf y 11)
(defun add-some (x)
(+ x y))
(defun add-ten (x)
(let ((y 10))
(add-some x)))
在词汇口述中,您会发现y
中的add-some
是全局变量y
。 (add-ten 89)
的结果为100
,因为y
中的add-some
为10 y
,add-ten
内的99
无效。< / p>
如果它是一个动态Lisp,答案是*earmuffs*
,lambda的主体可以引用不在全局范围内的变量。他们的结果变得神奇而难以预测。错误可能会忘记绑定它们,或者多个函数会覆盖它,因此最终结果是非常错误的软件。
在Common Lisp中,您有动态变量并且不会产生错误,而是使用x
来识别它们。
在最初的Lisp文章中,John McCarthy在原始高阶函数maplist
和他用来实现diff
的匿名函数中使用相同的变量let
。结果是他的例子不起作用。我们讨论的是6行代码,并且重用变量会导致很难在动态范围的语言中找到错误。
从动态范围的
创建词汇Lisp 具有动态范围的{{1}}一直存在,直到代码完成在其中执行,并且let中的变量的引用可用于所有被调用的函数,除非它们本身被覆盖。你不会得到闭包,所以如果你想在动态范围的lisp中关闭,你需要自己制作框架。
Scheme最初是作为动态范围的Lisp(MacLisp)下的解释器编写的。使用宏(也许还有读取器宏),你可以使动态绑定的Lisp像词法绑定的那样工作(或者反过来),但是它不会像在词法上被绑定开始一样高效。
答案 1 :(得分:2)
在简单的Lisp代码中,它没有多大区别。动态绑定可能更符合早期Lisp方言的精神。词法绑定更多地符合功能编程的精神,我们经常将高阶函数与闭包结合使用。
有一些情况支持词汇绑定。
让我们看一下动态绑定和特殊变量。
自由变量在运行时获取其绑定
西尔维斯特在他的回答中描述了一个问题:自由变量可以通过动态绑定在运行时获取它们的值。
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.
同样,它不是一个闭包,我们不能只是传递它......
<强>摘要强>
我们真的想要一种语言,默认情况下我们可以使用闭包,编译器可以对未绑定的变量发出警告,并且在源代码中很容易发现大多数绑定。