2

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

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

我一直在思考如何问这个问题,“词法作用域会破坏一些优雅吗?” 更严格地说,我最好的想法是看看它们是否等效,即是否可以实现另一个。

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

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

4

2 回答 2

4

动态范围更容易实现,也许更容易理解,但它使代码更难阅读:

(setf y 11)

(defun add-some (x)
  (+ x y))

(defun add-ten (x)
  (let ((y 10))
    (add-some x)))

在词法 lisp 中,您会看到yinadd-some是全局变量y。的结果(add-ten 89)将是100因为y是 10 inadd-some并且本地y内部add-ten不做任何事情。

如果它是一个动态的 Lisp,答案将是99,lambda 的主体可以引用不在全局范围内的变量。它们的结果变得神奇且难以预测。错误可能会忘记绑定它们,或者多个功能将覆盖它,因此最终结果是非常有错误的软件。

在 Common Lisp 中,您有动态变量,并且不会犯错误*earmuffs*来识别它们。

在最初的 Lisp 论文中,John McCarthyx在最初的高阶函数maplist和他用来实现的匿名函数中都使用了相同的变量diff。结果是他的例子没有奏效。我们谈论的是 6 行代码,变量的重用在动态范围的语言中引入了难以发现的错误。

从动态范围的 Lisp 制作词法 Lisp

let动态范围一直存在,直到代码在其中执行完毕,并且对 let 中变量的引用可用于所有被调用的函数,除非它们本身被覆盖。你不会得到闭包,所以如果你想在动态范围的 lisp 中使用闭包,你需要自己制作框架。

Scheme 最初是作为解释器在动态范围的 Lisp (MacLisp) 下编写的。使用宏(可能还有 reader-macros),您可以使动态绑定的 Lisp 像词法绑定的一样工作(或相反),但它不会像从词法绑定开始那样有效。

于 2015-03-30T14:15:43.883 回答
2

在简单的 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.

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

概括

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

于 2015-03-30T15:37:27.993 回答