1

在 Lisp 中(我还在用 SBCL 学习 Lisp),局部变量是用 声明的let,作用域只在那个表达式内。这是为什么?与其他命令式语言(如 C/C++/Java...)不同,我们可以在其函数范围内的任何位置自由使用局部变量。

4

3 回答 3

6

只是对 let 是什么的另一个小见解。它基本上是“向后拼写”的匿名函数的应用程序。

我将使用 JavaScript 进行说明,因为它更像是 C 语言,并且很好地说明了这个概念。

(function(variableA, variableB){
  console.log("variableA = " + variableA);
  console.log("variableA * variableB = " + variableA * variableB);})(6, 7);

现在,让我们为这些部分命名:from functionto;}是函数定义。(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 ...)。但总体思路是一样的。

于 2012-05-01T09:08:02.177 回答
4

Lisp 有一个用于引入局部变量的结构。它设置了一个范围。您可以在任何允许使用表单的地方使用它。对此的两个主要结构是 LET 和 LET*。Ait 允许我们独立于函数定义局部变量。当我们需要局部变量时,我们不需要引入函数,并且我们使用它来使局部变量具有最小范围。

另请注意,DEFUN 允许您声明局部变量:它允许参数列表中的 &AUX 引入它们。

于 2012-05-01T07:41:56.213 回答
3

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*可以引用前面的绑定。

于 2012-05-03T19:15:09.233 回答