动态范围有哪些优势?

时间:2008-11-26 15:04:01

标签: scope lexical-scope dynamic-scope

我了解到static scoping是唯一理智的做事方式,而dynamic scoping是魔鬼的工具,而且只会导致解释器/编译器的不良实现。

然后我从Common Lisp vs. Scheme文章中看到了这个片段:

Both Lexically and Dynamically    Lexical scope only, per the standard.
scoped special vars.  Common      Dynamically scoped vars are provided
Lisp just wins on this point.     by some implementations as an extension
                                  but code using them is not portable.

     (I have heard the arguments about whether Dynamic scoping
      is or is not a Bad Idea in the first place.  I don't care. 
      I'm just noting that you can do things with it that you 
      can't easily do without it.)

为什么Common Lisp“只是赢得了这一点”?使用动态范围更容易做些什么?我真的无法证明曾经需要它/把它视为一件好事。

10 个答案:

答案 0 :(得分:34)

与其他所有内容一样,动态范围仅仅是一种工具。使用得当它可以使某些任务更容易。使用不当可能会引起漏洞和头痛。

我当然可以看到它的一些用途。可以消除将变量传递给某些函数的需要。

例如,我可能会在程序开头设置显示,并且每个图形操作都会假定显示。

如果我想在该显示内部设置一个窗口,那么我可以将该窗口'添加'到另外指定显示的变量堆栈,并且在此状态下执行的任何图形操作将转到窗口而不是作为一个整体展示。

这是一个人为的例子,通过将参数传递给函数可以同样做得很好,但当你看一些代码时,这种任务会让你意识到全局变量真的是一个更容易的方法,并且动态范围通过函数参数的灵活性为您提供了很多全局变量的完整性

- 亚当

答案 1 :(得分:14)

动态范围的主要风险是意外后果。动态范围使得范围遵循运行时堆栈,这意味着范围中的符号集要大得多,并且在任何符号使用时都不明显。动态范围的变量很像全局变量,只有每个变量可能有多个版本,只有最新的定义可见,隐藏所有其他变量。

动态范围,只要它很有用,它对于需要对运行时堆栈敏感的行为很有用。例如(一般来说,不是特定于Lisp或变体):

  • 异常处理 - 最常见的catch块是发生异常时“在范围内”的块
  • 安全性 - 基于.NET代码的安全性根据所谓的代码来决定某些特权API的可访问性。

依赖于它用于其他用途的问题在于它创建了隐含的依赖关系并且在词法上遥远的代码段之间进行耦合。通过这种方式,它也类似于全局变量,只会更糟(由于动态覆盖定义)。

答案 2 :(得分:6)

动态范围在某些特定于域的语言中很有用。特别是,它可以用于样式表语言。我的经验来自GNU TeXmacs样式表语言。

在此语言中,显示参数存储在动态范围的变量中。这些变量会影响其范围内每个原子的渲染,包括由范围内调用的函数生成的原子。

TeXmacs中的动态范围也用于标记交叉引用。用于交叉引用的锚点从其环境中获取其标签。例如,公式块中包含的锚将使用公式编号作为标签,而不是位于公式之后的锚的节号。

快来想想吧,unix环境变量也是动态范围的变量。尽管内部范围不能改变外部范围中变量的值。

作为Barry Kelly noted,动态范围也可用于实现关注调用范围的语言功能,例如异常处理或依赖于上下文的权限处理。在存在连续的情况下,可以输入和退出范围,而无需遍历调用堆栈。

答案 3 :(得分:6)

动态范围允许定义上下文功能。从这个意义上讲,它与现代框架中的依赖注入非常相似。 (例如,考虑使用依赖项注入定义注释Java类以允许对各种引用进行透明初始化。(参见spring或JPA等))

显然,动态范围确定了关于给定函数的调用站点的运行时特性的某些假设,这在编译(或设计)时无法得到保证。再次,按照现代(Java)框架组件的示例,如果在容器的受控(运行时)环境之外实例化这样的类,则很可能该类在其所需的依赖性下无法运行将不会被初始化(也称为注入)。

但同样明显的是,组件系统(仅作为一个例子)明显受益于动态绑定机制。依赖注入是实现这一目标的框架级手段。动态范围是一种语言级别的手段。

答案 4 :(得分:4)

答案 5 :(得分:2)

我认为Common LISP中的动态范围类似于C中的全局变量。在功能函数中使用它们是有问题的。

答案 6 :(得分:1)

动态范围变量是一种强大的,但有时也是一种非常危险且危险的工具。

想象一下,您希望拥有特定于线程的全局变量,即每个线程都有自己的全局变量集。这可以通过动态范围轻松完成。只需在线程初始化时更改对此变量的引用。

或者考虑例外:它们在大多数语言中都是动态范围的。如果必须从头开始构建异常系统,则可以使用动态范围的变量轻松实现。

答案 7 :(得分:1)

动态范围打破Referential Transparency,这意味着您无法再对该程序进行推理。 DS基本上是类固醇的全局变量。

答案 8 :(得分:1)

Richard Stallman(GNU / Linux,Emacs,FSF)的这篇经典文章解释了为什么动态范围对Emacs编辑器和Emacs Lisp语言很重要。总之,它对定制很有用。

http://www.gnu.org/software/emacs/emacs-paper.html#SEC17

有关在Emacs Lisp中使用动态范围的更多信息,请参阅Emacs wiki上的this page

答案 9 :(得分:0)

以Emacs的方式绑定方便我的一个例子 - 不确定词汇或动态是否是正确的术语BTW。

let中的变量绑定向下看,没有明确的移交作为参数,这可以节省大量的击键。

(defun foo1 ()
  (message "%s" a))

(defun foo2 ()
  (let ((a 2))
  (message "%s" a)))

(defun foo3 ()
  (let ((a 1))
    (foo1)
    (foo2)))

==>
1
2

在foo2中绑定是有意义的,因为可能会在此处安装默认值

(let((a(if(eq something a)另有指定......