scheme语言中,let 和 let* 的用法
在 Scheme 语言中,let 和 let* 都是用来创建局部变量的语法,但它们在绑定变量的求值顺序上有着关键的区别。理解这个区别对于编写正确和高效的 Scheme 代码至关重要。
1. let 的用法与工作原理
let 是一种并行(parallel) 绑定。这意味着它在创建局部变量时,所有变量的绑定值都是在同一时刻(在语义上)被求值的。在求值过程中,任何一个绑定表达式都无法访问到它同一 let 表达式中定义的其他局部变量。
语法格式
(let ((var1 exp1)
(var2 exp2)
...)
body-expression)
工作原理
- 求值所有表达式:Scheme 首先会求值所有的
exp1,exp2, ...。这个求值过程是独立的,exp2无法使用var1的值,反之亦然。 - 创建新的环境:在所有表达式求值完成后,Scheme 会创建一个新的环境。
- 绑定变量:将求得的值分别绑定到对应的
var1,var2, ...上。 - 执行主体:在新的环境中执行
body-expression。
示例
考虑以下代码:
(let ((x 10)
(y (+ x 5))) ; 这里的 x 无法访问 let 中定义的 x
(+ x y))
运行这段代码会报错。为什么?因为在 y 的绑定表达式 (+ x 5) 中,x 指向的是外部环境中已有的 x(如果存在的话),而不是 let 内部正在定义的那个 x。如果外部没有 x,就会引发未定义变量的错误。
正确使用 let 的例子:
(define a 5)
(let ((x 10)
(y (+ a 2))) ; 这里的 a 指向外部定义的 a
(display x) ; => 10
(newline)
(display y) ; => 7
(newline)
(+ x y)) ; => 17
在这个例子中,y 的绑定表达式 (+ a 2) 成功访问了外部环境中的 a。
2. let* 的用法与工作原理
let* 是一种顺序(sequential) 绑定。与 let 不同,let* 按照从上到下的顺序逐一绑定变量。这意味着后面的绑定表达式可以使用前面已经定义好的局部变量。
语法格式
(let* ((var1 exp1)
(var2 exp2)
...)
body-expression)
工作原理
- 逐一求值和绑定:Scheme 依次处理每个变量绑定。
- 首先,求值
exp1并将其绑定到var1。 - 然后,在
var1已经绑定的新环境中,求值exp2并将其绑定到var2。 - 这个过程一直持续到所有变量绑定完成。
- 首先,求值
- 执行主体:在包含了所有新绑定的环境中执行
body-expression。
示例
现在,我们用 let* 来重写上面那个会报错的例子:
(let* ((x 10)
(y (+ x 5))) ; 这里的 x 可以访问上面定义的 x
(+ x y))
这段代码可以正常运行。
- 首先,
x被绑定到10。 - 然后,在
x已经为10的环境中,求值y的绑定表达式(+ x 5),得到15。 y被绑定到15。- 最后,在
x为10、y为15的环境中,求值(+ x y),得到25。
3. 何时使用 let,何时使用 let*?
- 使用
let:当你的局部变量定义彼此独立,且不需要互相依赖时,let是更好的选择。它在语义上更清晰,也可能因为可以并行求值而更具效率。 - 使用
let*:当一个局部变量的定义需要依赖于它前面定义的另一个局部变量时,必须使用let*。
总结
| 特性 | let (并行绑定) |
let* (顺序绑定) |
|---|---|---|
| 求值顺序 | 所有绑定表达式并行求值,不能互相引用。 | 所有绑定表达式按顺序求值,后面的表达式可以引用前面的变量。 |
| 应用场景 | 变量定义彼此独立,无依赖关系。 | 变量定义有依赖关系,需要使用前面定义的变量。 |
| 示例 | (let ((x 1) (y 2)) (+ x y)) |
(let* ((x 1) (y (+ x 1))) (+ x y)) |
理解 let 和 let* 的区别是掌握 Scheme 编程的关键一步。

浙公网安备 33010602011771号