为什么静态范围通常被认为比动态范围更好



我不确定我是否正确理解,为什么在旧版本的 Lisp 中没有实现静态作用域,只有动态作用域。发明Scheme的Sussman和Guy L. Steele Jr.只实现了Scheme的静态范围。

我发现有时静态变量使用起来更方便,因为它们可以用作完美的状态持有者,尽管我们应该小心避免不希望的名称冲突,因为这是不希望的副作用。

我知道静态范围是在编译时检测到的,而动态范围仅在运行时检测到。而动态范围被认为是难以欺骗的,有时甚至是推理的。

如果我们把上面提到的事实放在一边,我不确定我是否理解为什么静态范围通常被认为比动态范围更好?

动态作用域的基本问题是它不是组合的,因此违反了抽象。特别是,一段代码(例如,一个函数)的行为通常取决于从何处调用它,以及在调用者的站点上可见的定义。因此,调用方必须小心不要定义与被调用方使用的(非本地)名称冲突的名称。因此,调用方必须知道每个被调用方的实现详细信息。这使得模块化变得糟糕。特别是,对函数实现的更改可能会中断所有调用方。

词法范围对程序员来说更好,因为它使他们能够更好地推理他们的程序。特别是,他们可以从程序的源代码(本地)而不是从其动态行为中推断程序。

此外,还可以使用动态绑定功能(如参数)向语言添加受约束形式的动态作用域。SRFI-39是该计划的一般提案。另请参阅球拍指南中的参数。这使您可以在词法范围的语言中获得动态范围的大部分好处(这是更好的默认值)。

考虑以下定义:

(define (make-adder n)
  (lambda (x) (+ x n)))

现在,当你调用(make-adder 2) 时,你会得到一个函数。 这个函数有什么作用? 一起来看看:

(let ((add2 (make-adder 2)))
  (let ((n 10))
    (add2 n)))
;=> ?

上面的代码将计算出什么? 这取决于范围规则:

  1. 在词法范围的方案中,结果将为 12,因为 make-adder 定义中的n与示例代码中的n不同。
  2. 在(假设的)动态范围方案中,结果将为 20,因为在调用 +n已反弹到 10

现在,这两种行为都遵循简单、可预测的规则。 但请注意,在词法范围内的情况下,您可以查看make-adder的定义,并能够将n引用与其声明相关联,而无需知道将使用make-adder的上下文。 在动态范围的情况下,情况并非如此。 (事实上,在动态范围下,make-adder的参数是完全多余的。

正因为如此,在推理make-adder的行为时,词汇范围是一个优势,这就是为什么它通常(但并非总是)是首选的原因。

最新更新