阅读"为什么FUNARG问题应该被称为环境问题"我问自己一个问题:如果我们禁止定义一个不能创建闭包的函数会发生什么,即。
(define (foo x) (+ a x))
不允许在顶级(因为没有可以关闭的环境,包含 a ),而
(define (bar a) (lambda (x) (+ a x)))
允许,因为返回的lambda可以创建一个闭包。
这里有两个问题:
1.这可能会如何影响语言的表达能力?是否有某些功能因此限制而无法使用?有人举个例子,用自由变量定义函数会有用吗?
2.这是否意味着闭包中的所有环境变量现在都具有静态和可预测的偏移量?
答案 0 :(得分:1)
如 Will Ness 和 tfb 所述,有一个全球环境。在您的表达式中,+
和a
都是自由变量。重要的是,在定义a
时,您的全局环境中是否存在f
。
此外,“自由”或“约束”是一个相对的概念。
引用未在任何环境中绑定的词汇变量几乎没有意义。我能想到的唯一例子与元编程有关,但这并不是真正相关的,因为在这种情况下,你只是将代码作为数据来操作。当您最终生成表单并对其进行编译或评估时,您仍然具有词法范围,然后必须将所有符号解析为已知变量。 使用Common Lisp中的特殊变量或Emacs动态范围的默认变量,引用自由变量是有意义的。在Emacs中,您甚至可以在没有警告的情况下引用未声明的变量。
这可能会如何影响语言的表达能力?
因此,如果您不允许自由变量(未绑定在任何词法范围中),您基本上不允许使用动态范围的变量(在Common Lisp中,它们被定义为具有不确定范围和动态程度)。你失去了表现力。例如,在OCaml中就是这种情况。但是,您仍然可以定义一个库来模拟它们,如Delimited Dynamic Binding及其implementation中所示。
OCaml提供hyper-static global environment,它不仅使用词法范围,而且禁止更改现有绑定。
# let a = 10;;
val a : int = 10
# let f () = a;;
val f : unit -> int = <fun>
# let a = 20;;
val a : int = 20
# f ()
- : int = 10
上方第二个a
阴影位于前一个,但f
仍指向前一个a
。这也是函数的情况,这就是为什么有rec
关键字来定义递归和相互递归函数的原因。
这是一种与Lisp不同的方法,值得注意的是允许在运行时重新定义大多数内容。
这是否意味着闭包中的所有环境变量现在都具有静态和可预测的偏移量?
词法范围允许将变量编译到固定位置。是否完成取决于您的工具。例如,在解释器中,您的环境可能保留在运行时数据结构中。