Variables 为什么';让';范围有限?

Variables 为什么';让';范围有限?,variables,lisp,scope,let,Variables,Lisp,Scope,Let,在Lisp中(我仍在学习Lisp和SBCL),局部变量是用let声明的,范围仅在该表达式中。为什么呢?与其他命令式语言(如C/C++/Java…)不同,在C/C++/Java…,我们可以在其函数范围内的任何位置自由使用局部变量。Lisp有一个用于引入局部变量的构造。它设置了一个范围。您可以在任何允许使用表单的地方使用它。这方面的两个主要构造是LET和LET*。Ait允许我们独立于函数定义局部变量。当我们需要局部变量时,我们不需要引入函数,我们使用它使局部变量的作用域最小 还要注意,DEFUN允许

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

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


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

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


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

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

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

(function(variableA, variableB){
  console.log("variableA = " + variableA);
  console.log("variableA * variableB = " + variableA * variableB);})(6, 7);
现在,让我们命名这些部分:从
函数
;}是函数定义<代码>(定义)(参数)
是应用程序。Let表达式的作用本质上是相同的,即它调用一个带有参数的匿名函数,您可以在该函数中使用这些参数作为变量。所以,如果你考虑前面的例子,用Error表格重写它会产生一些类似的情况:

(let(variableA = 6, variableB = 7){
  console.log("variableA = " + variableA);
  console.log("variableA * variableB = " + variableA * variableB);});
(JavaScript还不支持let,所以上面的代码示例不起作用,但应该是一个示例)


您还应该注意,它并不是那么简单。因为在更复杂的情况下,您可能希望在构造另一个参数时引用其中一个参数,然后使用
(let*…)
,或者您希望使用函数作为此表达式的参数,然后使用
(flet…
(labels…
)。但总的想法是一样的。

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

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

(function(variableA, variableB){
  console.log("variableA = " + variableA);
  console.log("variableA * variableB = " + variableA * variableB);})(6, 7);
现在,让我们命名这些部分:从
函数
;}是函数定义<代码>(定义)(参数)
是应用程序。Let表达式的作用本质上是相同的,即它调用一个带有参数的匿名函数,您可以在该函数中使用这些参数作为变量。所以,如果你考虑前面的例子,用Error表格重写它会产生一些类似的情况:

(let(variableA = 6, variableB = 7){
  console.log("variableA = " + variableA);
  console.log("variableA * variableB = " + variableA * variableB);});
(JavaScript还不支持let,所以上面的代码示例不起作用,但应该是一个示例)


您还应该注意,它并不是那么简单。因为在更复杂的情况下,您可能希望在构造另一个参数时引用其中一个参数,然后使用
(let*…)
,或者您希望使用函数作为此表达式的参数,然后使用
(flet…
(labels…
)。但是总的想法是一样的。

让和你所指的其他风格之间的区别纯粹是句法上的糖分。在一种可以引入新变量“anywhere”的语言中,仍然存在嵌套的作用域,只是它们在表面语法中是平坦的。比如说

{
  x = 3;
  x++;
  y = 4;
  print "hi";
  z = 42;
}
这里不仅有一个作用域,还有不同的作用域。有一个以
x=3
开头的
x
范围。然后是以
y=4
开头的
y
的范围。如果在该范围之前引用
y
,则会出现错误

Lisp中没有这类内容,因为它为处理代码的代码的开发增加了很多工作。这种语言的编译器必须分析代码并恢复抽象语法树结构,该结构显示嵌套的
let
结构,因此编译器的剩余过程不必继续重新分析代码以重新发现该信息

在Lisp中,我们直接在抽象语法树结构中编写,就是这样。这使得语言统一。一切都是一种形式,如果它是一种复合形式,它在最左边的位置有一个操作符,它以一种上下文无关的方式决定该形式其余部分的含义

例如,请参见最近的问题:。如果在lambda表达式中通过绑定特殊形式(如
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*
中后期的初始化可以引用早期的绑定。

let和您所引用的其他样式之间的区别纯粹是语法上的差异。在一种可以在“任何地方”引入新变量的语言中,仍然存在嵌套的作用域,只是它们被展平了