在 Lisp 中(我还在用 SBCL 学习 Lisp),局部变量是用 声明的let
,作用域只在那个表达式内。这是为什么?与其他命令式语言(如 C/C++/Java...)不同,我们可以在其函数范围内的任何位置自由使用局部变量。
3 回答
只是对 let 是什么的另一个小见解。它基本上是“向后拼写”的匿名函数的应用程序。
我将使用 JavaScript 进行说明,因为它更像是 C 语言,并且很好地说明了这个概念。
(function(variableA, variableB){
console.log("variableA = " + variableA);
console.log("variableA * variableB = " + variableA * variableB);})(6, 7);
现在,让我们为这些部分命名:from function
to;}
是函数定义。(definition)(arguments)
是应用程序。Let 表达式本质上做同样的事情,即它调用一个带有参数的匿名函数,您在该函数内部将其用作变量。所以,如果你考虑前面的例子,用 let 形式重写它会变成这样:
(let(variableA = 6, variableB = 7){
console.log("variableA = " + variableA);
console.log("variableA * variableB = " + variableA * variableB);});
(JavaScript 还不支持 let,所以上面不是一个有效的代码示例,但它应该是一个说明)
您还应该注意,它并不是那么简单。因为在更复杂的情况下,您可能希望在构造另一个参数时引用其中一个参数 - 然后您将使用(let* ...)
, 或者您希望将函数用作此表达式的参数,然后您将使用(flet ...)
or (labels ...)
。但总体思路是一样的。
Lisp 有一个用于引入局部变量的结构。它设置了一个范围。您可以在任何允许使用表单的地方使用它。对此的两个主要结构是 LET 和 LET*。Ait 允许我们独立于函数定义局部变量。当我们需要局部变量时,我们不需要引入函数,并且我们使用它来使局部变量具有最小范围。
另请注意,DEFUN 允许您声明局部变量:它允许参数列表中的 &AUX 引入它们。
let
与您所指的其他样式之间的区别纯粹是语法糖之一。在可以在“任何地方”引入新变量的语言中,仍然存在嵌套范围,只是它们在表面语法中被展平。例如
{
x = 3;
x++;
y = 4;
print "hi";
z = 42;
}
这里不只有一个范围,而是不同的范围。有一个以x
开头的范围x = 3
。然后有一个以y
开头的范围y = 4
。如果您在该范围之前引用y
,则会出现错误。
这种事情被 Lisp 排除在外,因为它为处理代码的代码的开发增加了很多精力。这种语言的编译器必须分析该代码并恢复显示嵌套let
结构的抽象语法树结构,以便编译器的剩余通道不必继续重新分析代码以重新发现该信息。
在 Lisp 中,我们直接用抽象的语法树结构编写,就是这样。这使得语言统一。一切都是一种形式,如果它是一种复合形式,它在最左边的位置有一个运算符,它以一种与上下文无关的方式确定该形式其余部分的含义。
例如,请参阅这个最近的问题:find free variables in lambda expression 。如果通过绑定特殊形式(例如let
,或通过扩展为此类特殊形式的小程序集的宏)来触发 lambda 表达式中的自由变量,则要容易得多。
另请注意,尽管 Lisp 无论如何都没有强制执行函数式风格,但它比那些类型的语言更普遍(注意我在虚构的“{} 语言”中的变量定义中有一些命令式语句。)如果你编写代码其中没有命令式语句,那么您就不会真正遇到这种情况。
在功能代码中,您可以采用以下形式进行连续绑定let*
:
(let* ((x 1) x = 1;
(y (1+ x)) y = x + 1;
(z (/ x y)) z = x / y;
(sqrt z)) return sqrt(z);
后面的初始化let*
可以引用前面的绑定。