大师们到处说:“动态作用域比词法作用域更强大。 ”但直到现在我还没有看到一个让我信服的简洁示例。
2 回答
多年前,当我使用 Clipper5 语言(原始 DBIII 编程语言的扩展实现)时,我已经习惯了动态作用域。
如果使用得当,动态作用域非常有用,因为它允许在调用链中传递参数,而无需让中间函数知道它们。这使得仅在所需位置(即参数的来源和使用参数的位置)添加例如新参数变得容易。
假设您有一个调用“print-shipment-documentation”的“close-order”,该调用调用“print-invoice-row”的“print-invoice”。如果您需要添加一个影响发票行打印方式的新参数,您可以在顶级“关闭订单”函数中添加一个用户选项,使用该值设置一个动态变量并仅处理该值在“打印发票行”中。
函数接口链将保持该参数的“干净”,仅列出要传递的基本数据。
这类似于使用全局变量,但它是“正确的”,因为它在调用后不会保持“脏”,并且在大多数支持线程的实现中,它甚至可以从多个线程中使用而不会出现问题(每个线程都有自己的值)。
过去我发现将这种方法用于“配置”值的能力非常有用。但是,您必须注意避免不必要的名称冲突。
另一个很好用的例子是*standard-output*
在 Common Lisp 中,即一个值,否则你需要存储在全局中(就像 C 语言一样),或者另一个明显不切实际的替代方案是将它传递给可能需要它的每个函数(直接使用它或调用可能需要它的函数)。
制作*standard-output*
一个动态变量可以让 Common Lisp 程序避免在任何地方引用它,但仍然保持在需要时将标准输出重定向到其他东西的能力(并且以比 C 更简洁的方式)。
我最喜欢的例子在 Emacs 论文中得到了很好的解释:http ://www.gnu.org/software/emacs/emacs-paper.html#SEC17
动态范围很有用。考虑一下编辑图片功能,它是用来临时对某些编辑命令稍作改动,以便更方便地编辑排列成二维图片的文本。例如,更改打印字符以替换现有文本,而不是将其推到右侧。编辑图片的工作原理是动态绑定参数变量的值,然后将编辑器作为子程序调用。编辑器“退出”命令导致返回到编辑图片子例程,该子例程立即返回到编辑器的外部调用。在此过程中,动态变量绑定被取消。
动态绑定对于命令调度表的元素特别有用。例如,用于编写消息回复的 RMAIL 命令临时定义字符 Control--Meta--Y 以将原始消息的文本插入到回复中。始终定义实现此命令的函数,但 Control--Meta--Y 不会调用该函数,除非正在编辑回复。回复命令通过动态绑定 Control--Meta--Y 的调度表条目然后将编辑器作为子例程调用来实现这一点。当编辑器的递归调用返回时,用户编辑的文本作为回复发送。
动态范围不必是唯一提供的范围规则,只要它可用就可以了。