日期:2020-07-10我一直不太适合动态作用域,因为它是用常见的Lisp、Emacs Lisp或LaTeX实现的。在我看来,动态作用域似乎是现代编程中一种几乎被遗忘的技术和被抛弃的技术。虽然它比词法作用域更难理解,也更神奇,但也有一些应用程序受益于动态作用域。
在词法作用域中,如果您访问变量,编译器将开始从引用点向外按词法顺序搜索声明。这意味着它首先在最内部的范围内进行搜索,然后通过闭合嵌套的方式继续扩大其搜索范围。例如,在下面的代码片段中,对foo()的调用将返回23,因为词法上最接近的声明是全局变量。
Int config=23;return foo(){return config;}void bar(){int config=42;printf(";%d\";,foo());}。
使用动态作用域,世界看起来会有所不同,因为给定名称的最接近(时间方向)动态绑定是灵活最接近的。在本例中,foo()将返回42,因为bar()中的绑定比全局绑定发生得更晚。
您可以将动态作用域的全局变量视为可阴影全局绑定。通过为变量创建新绑定,我们可以隐藏旧绑定,子函数中的所有引用现在都将引用新绑定,尽管它们尚未通过参数传输收到值。因此,动态作用域的变量是可以影响函数行为的影子侧通道。
例如,在Common Lisp中,变量*STANDARD-OUTPUT*是动态作用域,函数(打印)只是将字符发送给该变量。因为它是动态限定作用域的,所以我们可以通过为*Standard-output*创建一个新的绑定,将所有输出直接定向到一个文件。
比方说,用C++来做同样的事情不是很好吗?幸运的是,使用模板、RAII模式和一些模板魔术,我们可以编写如下代码:
DynamicScope<;int>;G(0);void bar(){std::cout<;<;";bar";<;*G<;<;std::Endl;}void Foo(){DynamicScope<;int>;::BindInstance_(G,3);bar();}int main(){std::cout<;<;&34;main1";<;<;*G<;<;std::Endl;foo();std::cout<;";main2";<;<;*G<;<;std::Endl;}