然后我在 Common Lisp vs. Scheme 文章中看到这个代码片段:
既具有词法作用域,也具有动态作用域 标准仅支持词法作用域。 特殊变量。Common Lisp的 动态作用域变量由一些实现提供 在这一点上胜出。 作为扩展,但使用它们的代码不具备可移植性。为什么Common Lisp“在这一点上胜出”?使用动态作用域更容易做什么?我真的无法证明它是必要的/它是一个好事。
(我听说过关于动态作用域是否是个好主意的争论。我不在乎。我只是指出你可以用它做一些不容易做到的事情。)
既具有词法作用域,也具有动态作用域 标准仅支持词法作用域。 特殊变量。Common Lisp的 动态作用域变量由一些实现提供 在这一点上胜出。 作为扩展,但使用它们的代码不具备可移植性。为什么Common Lisp“在这一点上胜出”?使用动态作用域更容易做什么?我真的无法证明它是必要的/它是一个好事。
(我听说过关于动态作用域是否是个好主意的争论。我不在乎。我只是指出你可以用它做一些不容易做到的事情。)
和其他一切一样,动态作用域仅仅是一种工具。如果使用得当,可以使某些任务更容易完成。如果使用不好,可能会引入错误和头疼问题。
我确实可以看到它的一些用途。可以消除将变量传递给某些函数的需求。
例如,我可能在程序开头设置显示,并且每个图形操作都假定这个显示。
如果我想在该显示内设置一个窗口,则可以将该窗口“添加”到除了指定显示外的变量堆栈中,在这种状态下执行的任何图形操作都将转到窗口而不是整个显示。
这是一个刻意制造的例子,同样可以通过将参数传递给函数来很好地完成,但当您查看某些代码生成的任务时,就会意识到全局变量确实是一种更容易的方式,而动态作用域为您提供了全局变量的清晰性和函数参数的灵活性。
-Adam
动态作用域在某些领域特定语言中非常有用。特别是在样式表语言中,它可以方便地使用。我的经验来自于GNU TeXmacs 样式表语言。
在这种语言中,显示参数存储在动态作用域变量中。这些变量会影响其作用域内的每个原子的呈现效果,包括通过在作用域内调用的函数生成的原子。
TeXmacs 中的动态作用域还用于标记交叉引用。用于交叉引用的锚点从其环境获取其标签。例如,包含在公式块中的锚点将使用公式编号作为标签,而不是在公式之后的锚点使用章节编号。
想想看,Unix 环境变量也是动态作用域变量。尽管内部作用域不能更改外部作用域变量的值。
正如Barry Kelly 指出的那样,动态作用域也可以用于实现关注调用范围的语言特性,例如异常处理或上下文相关的权限处理。在存在连续性的情况下,可以进入和退出作用域而无需遍历调用堆栈。
Date: Thu, 14 Aug 2003 08:05:44 -0700
From: Michael Vanier
Subject: Re: bindings and assignments (was: Re: continuations)
Date: Thu, 14 Aug 2003 10:45:34 -0400
From: "David B. Tucker"
I imagine, though I don't have statistical evidence, that the requirement of declaring local variables to be final in order to reference them within anonymous inner classes (closures) is almost entirely unknown and unused in practice.
Out of curiosity, does anyone know why Java only allows final variables to be referenced from within anonymous classes?
Dave
<cynic>Otherwise you'd have the equivalent of true closures, and if you had that java would be a *really* powerful and useful language, so they obviously couldn't do that. </cynic>
Actually, the prototype implementation did allow non-final variables to be referenced from within inner classes. There was an outcry from users, complaining that they did not want this! The reason was interesting: in order to support such variables, it was necessary to heap-allocate them, and (at that time, at least) the average Java programmer was still pretty skittish about heap allocation and garbage collection and all that. They disapproved of the language performing heap allocation "under the table" when there was no occurrence of the "new" keyword in sight.
所以,在早期,Java采用了一种“第三种”方法(与我上面提到的两种方法相对),即不是“只读副本”,也不是在闭包定义时实时访问封闭的(可变)状态,而是状态的可变副本(至少,我是这样理解引用的段落的;或者不是的话,他是在谈论堆分配仅仅是引用吗?..那么这就是第二个选项。第三个选项对我来说确实看起来不合理)。我不确定现在他们如何在Java中实现闭包,我没有关注Java的最新内容。
http://www.gnu.org/software/emacs/emacs-paper.html#SEC17
另请参见Emacs维基上的this page,了解有关在Emacs Lisp中使用动态作用域的更多信息:动态作用域变量是一种强大但有时也不直观和危险的工具。
想象一下,您想要具有特定于线程的全局变量,即每个线程都有自己的一组全局变量。这可以很容易地通过动态作用域来实现。只需在线程初始化时更改对这些变量的引用即可。
或者考虑异常:它们在大多数语言中都是动态作用域的。如果您必须从头开始构建异常系统,则可以轻松使用动态作用域变量来完成。
Emacs绑定方式对我而言的一个例子 - 顺便提一下,不确定lexical或dynamic是正确术语。
在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) assign otherwise...