我已经编程了很长时间(实际上太长了),但我真的很难掌握“自由变量”和“绑定变量”这两个术语。
我在网上找到的大多数“解释”都是从谈论诸如 Lambda 演算和形式逻辑或公理语义之类的话题开始的。这让我想去拿我的左轮手枪。
有人可以从实现的角度解释这两个术语吗?它们可以存在于编译语言中,它们翻译成什么低级代码?
我已经编程了很长时间(实际上太长了),但我真的很难掌握“自由变量”和“绑定变量”这两个术语。
我在网上找到的大多数“解释”都是从谈论诸如 Lambda 演算和形式逻辑或公理语义之类的话题开始的。这让我想去拿我的左轮手枪。
有人可以从实现的角度解释这两个术语吗?它们可以存在于编译语言中,它们翻译成什么低级代码?
自由变量是在某些函数中使用的变量,其值取决于调用、调用或使用函数的上下文。例如,在数学术语中,z
是一个自由变量,因为它不受任何参数的限制。x
是有界变量:
f(x) = x * z
在编程语言术语中,自由变量是在运行时动态确定的,在函数调用堆栈上向后搜索变量的名称。
有界变量评估不依赖于函数调用的上下文。这是最常见的现代编程语言变量类型。局部变量、全局变量和参数都是有界变量。
自由变量有点类似于一些古老编程语言的“按名称传递”约定。
假设您有一个f
只打印一些变量的函数:
def f():
print(X)
这是 Python。虽然X
不是局部变量,但它的值遵循 Python 约定:它在定义函数的块链上向上搜索,直到到达顶层模块。
由于在 Python 中 的值X
是由函数声明上下文决定的,所以我们说它X
是一个有界变量。
假设,如果X
是一个自由变量,这应该打印 10:
X = 2
def f():
print(X)
def g():
# X is a local variable to g, shadowing the global X
X = 10
f()
在 Python 中,此代码打印 2,因为这两个X
变量都是有界的。局部X
变量 ong
绑定为局部变量,而 onf
绑定到全局变量X
。
使用自由变量的编程语言的实现需要注意每个函数被调用的上下文,并且对于每个自由变量使用一些反射来查找要使用的变量。
自由变量的值通常不能在编译时确定,因为很大程度上取决于运行时流程和调用堆栈。
变量是自由的还是绑定的都是相对的;这取决于您正在查看的代码片段。
在这个片段中,x
被绑定:
function(x) {return x + x;};
在这里,x
免费发生:
return x + x;
换句话说,自由度是上下文的属性。您没有说“ x是一个自由变量”或“ x是一个绑定变量”,而是确定您正在谈论的上下文:“ x在表达式E中是自由的”。因此,同一个变量x可以是自由的也可以是绑定的,具体取决于您所讨论的代码片段。如果片段包含变量的绑定站点(例如,它在函数参数中列出),那么它是绑定的,如果没有,它是免费的。
从实现的角度来看,自由/绑定区别很重要的地方是当您实现变量替换的工作方式时(例如,当您将参数应用于函数时会发生什么。)考虑评估步骤:
(function(x) {return x + x;})(3);
=> 3 + 3
=> 6
这很好用,因为x
在函数体中是免费的。然而,如果x
被绑定在函数体中,我们的评估需要小心:
(function(x) {return (function(x){return x * 2;})(x + x);})(3);
=> (function(x){return x * 2;})(3 + 3); // careful to leave this x alone for now!
=> (function(x){return x * 2;})(6);
=> 6 * 2
=> 12
如果我们的实现没有检查绑定事件,它可能已经替换了绑定 x3
并给了我们错误的答案:
(function(x) {return (function(x){return x * 2;})(x + x);})(3);
=> (function(x){return 3 * 2;})(3 + 3); // Bad! We substituted for a bound x!
=> (function(x){return 3 * 2;})(6);
=> 3 * 2
=> 6
此外,应该澄清的是,自由与绑定是语法(即代码本身)的属性,而不是在运行时如何评估代码的属性。vz0 讨论动态范围的变量,它与自由变量有些相关,但不是同义词。正如 vz0 正确描述的那样,动态变量范围是一种语言功能,它允许通过查看运行时调用堆栈来评估包含自由变量的表达式,以找到共享相同名称的变量的值。然而,在不允许动态范围的语言中谈论自由出现的变量仍然是有意义的:如果你试图在这些语言中评估这样的表达式,你只会得到一个错误(比如“ x未定义”)语言。
而且我无法克制自己:我希望有一天,当人们提到 lambda 演算时,你能找到把左轮手枪收起来的力量!Lambda 演算是考虑变量和绑定的好工具,因为它是一种极简的编程语言,只支持变量和替换,仅此而已。现实世界的编程语言包含许多其他垃圾(例如动态范围),它们掩盖了本质。