用于动态范围?

hat*_*rix 8 lisp scope elisp

我一直在用emacs lisp弄湿手,有时让我感到震惊的是动态范围.它有很多未来吗?我所知道的大多数语言都使用静态作用域(或者已经转向静态作用域,比如Python),可能因为我知道它更好,我倾向于喜欢它.是否存在动态范围更有用的特定应用程序/实例或示例?

Eli*_*sky 14

有这个问题的一个很好的讨论在这里.与您的问题相关的最有用的部分是:

动态绑定非常适合修改子系统的行为.假设您使用的函数'foo'使用'print'生成输出.但有时你想在你选择的缓冲区中捕获输出.使用动态绑定,很容易:

(let ((b (generate-new-buffer-name " *string-output*"))))
    (let ((standard-output b))
      (foo))
    (set-buffer b)
    ;; do stuff with the output of foo
    (kill-buffer b))
Run Code Online (Sandbox Code Playgroud)

(如果你经常使用这种东西,你可以将它封装在一个宏中 - 但幸运的是它已经完成了'with-output-to-temp-buffer'.)

这是因为'foo'使用名称'standard-output'的动态绑定,所以你可以用自己的绑定替换该名称来修改'foo'的行为 - 以及'foo'调用的所有函数.

在没有动态绑定的语言中,您可能会在'foo'中添加一个可选参数来指定缓冲区,然后'foo'会将其传递给任何对'print'的调用.但如果'foo'调用其他自称为'print'的函数,你也必须改变这些函数.如果'print'有另一个选项,比如'print-level',你必须将它添加为可选参数...或者,你可以记住'标准输出'的旧值,替换你的新值,调用'foo'然后恢复旧值.并记得使用'throw'处理非本地出口.当你完成这个时,你会发现你已经实现了动态绑定!

也就是说,词汇绑定对于99%的案例来说恕我直言更好.请注意,现代Lisps不像Emacs lisp那样是动态绑定的.

  • Common Lisp支持两种形式的绑定,尽管词法更常用
  • Scheme规范甚至没有指定动态绑定(只有词法),尽管许多实现都支持这两种.

此外,Python和Ruby之类的现代语言在某种程度上受到Lisp的启发,通常以直接的方式支持词法绑定,动态绑定也可用,但不那么简单.


Tre*_*son 7

如果您阅读Emacs论文(1981年编写),则会有一个特定部分"可扩展性的语言功能"来解决这个问题.在Emacs中,还有缓冲区本地(文件本地)变量的附加范围.

我引用了下面最相关的部分:

形式参数不能替换动态范围

一些语言设计者认为应该避免动态绑定,而应该使用显式参数传递.想象一下,函数A绑定变量FOO,并调用函数B,它调用函数C,C使用FOO的值.据说A应该将值作为参数传递给B,它应该将它作为参数传递给C.

但是,这不能在可扩展系统中完成,因为系统的作者无法知道所有参数是什么.想象一下,函数A和C是用户扩展的一部分,而B是标准系统的一部分.变量FOO在标准系统中不存在; 它是扩展的一部分.要使用显式参数传递,需要向B添加一个新参数,这意味着重写B和调用B的所有内容.在最常见的情况下,B是编辑器命令调度程序循环,从很多地方调用.

更糟糕的是,C还必须传递一个额外的参数.B没有按名称引用C(写B时C不存在).它可能在命令调度表中找到指向C的指针.这意味着有时调用C的同一调用同样可以调用任何编辑器命令定义.因此,必须重写所有编辑命令以接受并忽略其他参数.到现在为止,原来的系统都没有留下!