unj*_*nj2 12 lisp language-agnostic programming-languages language-theory logo-lang
动态范围语言的一些示例是什么?选择那个设计的原因是什么?是因为它易于实施吗?
小智 9
动态范围的语言更容易实现.要访问不在当前激活记录/堆栈帧中的变量,只需跟随控制链接即可.然后不需要静态/词汇访问链接,使堆栈帧更小.
动态变量在运行时可能是"不可预测的",因为需要知道实际的堆栈帧将以哪种顺序知道将使用哪个变量.仅通过查看代码的静态结构就无法获得此信息.如果在实施时不容易预测程序的实际调用图,则很容易被捕获.这就是为什么今天的大多数语言都有静态作用域(但大多数异常系统都是动态的,因为这是最实用的).
但是在某些情况下,动态范围的变量非常有用.例如,当重定向输出时,您可以使用动态变量为本地代码设置标准输出,并从那里调用所有代码.
(let ((*standard-output* *some-other-stream*))
(stuff))
Run Code Online (Sandbox Code Playgroud)
在这个common-lisp示例中(来自Seibel),标准输出在let表单的持续时间内(在其封闭的parens内)绑定到另一个流.当执行离开let时,它会回到事先的状态.请参阅http://gigamonkeys.com/book/variables.html Peter Seibels免费和优秀的书,Practical Common Lisp,进行了很好的讨论.在Seibels自己的话:
动态绑定使全局变量更易于管理,但重要的是要注意它们仍允许远距离操作.绑定全局变量有两个远距离效应 - 它可以改变下游代码的行为,并且还打开了下游代码将新值赋给在堆栈上建立的更高的绑定的可能性.只有在需要利用这些特征中的一个或两个时,才应使用动态变量.
Mathematica是另一种通过Block构造动态确定范围的语言.在使用公式时,这实际上非常有用.它允许你写的东西
In[1]:= expr = a*t^2 + b*t+ c;
In[2]:= Block[{a = 1, b = -1, c = 2}, Table[expr, {t, 5}]]
Out[2]= {2, 4, 8, 14, 22}
Run Code Online (Sandbox Code Playgroud)
如果变量喜欢a和t以词法作用的话,它根本不起作用.它与Mathematica的规则重写系统特别合作,除其他外,如果它没有现有的定义,它将保留变量(如符号表达式).
Mathematica可以使用该Module构造伪造词法作用域,但这实际上做的是用新的,据称唯一的符号重写表达式(如果你预测下一个唯一符号将会是什么,你可以引起冲突,这在大多数情况下很容易).这意味着
Module[{x = 4},
Table[x * t, {t, 5}]]
Run Code Online (Sandbox Code Playgroud)
将变成这样的事情:
Block[{x$134 = 4},
Table[x$134 * t, {t, 5}]
Run Code Online (Sandbox Code Playgroud)
Emacs Lisp,在其中一个库中,有一个构造(实际上是一个Lisp宏)lexical-let,它可以提供完全相同的技巧来伪造词法作用域.
当你编译你的语言而没有ELisp或Mathematica的虚假词汇时,真正的词法范围有性能优势,因为你需要在动态变量和它的当前值之间进行一些映射,这意味着要进行查找(通过哈希表或属性列表或其他内容)和附加的间接层.
编辑:如果只有词法变量,则可以通过在输入范围时存储全局词法变量的原始值并保证在退出范围时恢复旧值来伪造动态范围.为了确保这一点,你需要像Lisp UNWIND-PROTECT或finally块一样的东西.我已经看到使用C++析构函数完成这项工作,主要是作为练习.
嗯,有很多网站讨论利弊,所以我不会去那里。
XSLT 是一种有趣的语言,它的一些功能有点类似于动态作用域。虽然 XSLT 的模板和变量等是词法范围的,但 XSLT 当然都是关于 XML 的 - 并且 xml 树中的当前位置是“动态范围的”,因为上下文节点是全局的,因此不评估 XPath 表达式根据 XSLT 的词法范围,但根据其动态评估。