动态范围可以实现词法范围吗?

时间:2015-03-30 13:35:01

标签: lisp common-lisp lexical-scope

旧的Lisp,包括Common Lisp在某些时候,以及在emacs 24.1之前的elisp,是动态范围的,在我读过的资源中,似乎共识似乎是词法范围更适合编程。

我感觉动态范围合法地更容易理解。动态范围可以完全根据符号及其值来考虑。词汇范围需要一个新的概念"绑定"据我所知,它破坏了Lisp语言的一些简洁/优雅。

我一直在思考如何提出这个问题,"词法范围是否打破了一些优雅?"更加严格,我最好的想法是看看它们是否相同,即是否可以实现另一个。

是否可以将词法范围的lisp实现为动态范围列表中的简单扩展?如果没有解释器引入新的ad-hoc符号,是否可以这样做?

更具体地说,创建词法作用域的let可以在动态范围的lisp中实现吗?

2 个答案:

答案 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 yadd-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.

同样,它不是一个闭包,我们不能只是传递它......

<强>摘要

我们真的想要一种语言,默认情况下我们可以使用闭包,编译器可以对未绑定的变量发出警告,并且在源代码中很容易发现大多数绑定。