Sec 2.2 词汇绑定
如同 Algol 60 一样,绑定结构 let, let*, letrec, letrec*, let-values 和 let*-values 给出了 Scheme 的块结构。前四个结构的语法相同,但它们为变量绑定建立的区域不同。在 let 表达式中,初始值在任何变量绑定之前进行计算;在 let* 表达式中,绑定和求值按顺序执行;而在 letrec 和 letrec* 表达式中,当它们的初始值被计算时,所有的绑定有效,因此允许相互的递归定义。let-value 和 let*-value 结构分别和let、let*类似,但是设计用于处理多值表达式,绑定不同的标识符到返回值。
let ((variable init) ... ) expr expr ... [扩展的标准特殊形式]
初值在当前环境中计算(以未指定的顺序),variables 与保存结果的新位置绑定,exprs 在扩展环境中按顺序计算,最后一个 expr 的值被返回。每一个变量绑定都将 exprs 作为其区域。
MIT/GNU Scheme 允许省略任何初始化,在这种情况下,相应的变量是未赋值的。
请注意,以下是等效的:
(let ((variable init) ... ) expr expr ... )
((lambda (variable ... ) expr expr ... ) init ... )
一些例子:
(let ((x 2) (y 3))
(* x y)) => 6
(let ((x 2) (y 3))
(let ((foo (lambda (z) (+ x y z)))
(x 7))
(foo 4))) => 9
见 Sec 2.9 [ 迭代 ], 第29页,关于 "named let" 的信息
let* ((variable init) ...) expr expr ... [扩展的标准特殊形式]
let* 和 let 相似,但是绑定按从左到右顺序执行,绑定区域为 let* 中绑定右侧的表达式。因此,对于第一个绑定,它在相应环境中第二个绑定里是可以见的,以此类推。
请注意,以下是等效的:
(let* ((variable1 init1)
(variable2 init2)
...
(variableN initN)
expr
expr ...)
(let ((variable1 init1))
(let ((variable2 init2))
...
(let ((variableN initN))
expr
expr ...)
...))
一个例子:
(let ((x 2) (y 3))
(let* ((x 7)
(z (+ x y)))
(* z x))) =>70
letrec ((variable init) ... ) expr expr ... [ 扩展的标准特殊形式 ]
变量与未赋值的新位置绑定,初始值在扩展环境中计算(以不确定的顺序),每个变量按照对应的初始值结果进行赋值,表达式在扩展环境中依序计算,最后一个表达式的值被返回。每个变量的绑定区域为整个 letrec 表达式区域,从而使定义相互递归成为可能。
MIT/GNU Scheme 允许任意的初始值被省略,这样,对应的变量将不被赋值。
(letrec ((even?
(lambda (n)
(if (zero? n)
#t
(odd? (- n 1)))))
(odd?
(lambda (n)
(if (zero? n)
#f
(even? (- n 1))))))
(even? 88)) => #t
一个 letrec 的规则特别重要:可以在不指定或不引用任何变量值的情况下对每个初值进行计算。如果违反此规则,则为错误。该限制是必须的,因为 Scheme 按值而不是按名称传递参数。在 letrec 的常见用途中,所有的初值都是 lambda 或 delay 表达式,以上规则自动满足。
letrec* ((variable init) ... ) expr expr ... [ 扩展的标准特殊形式 ]
变量被绑定到新位置,每个变量按照从左到右的顺序根据对应的初值计算结果进行赋值 (交错计算和分配), exprs 在结果环境中计算,并返回最后一个 expr 的值。尽管有从左到右的的计算和赋值顺序,每一个变量绑定仍然以整个 letrec* 表达式区域为其区域,从而可以定义相互递归的过程。(翻译的不好,不是很明白 letrec 和 letrec *的使用语法到底是什么,后面明白一点的时候,再回来好好翻译准确)
如果不赋值或者引用对应变量的值或绑定中紧随其后的任何绑定的变量的情况下,无法计算每个初值,则这是个错误。另一个规则是:多次调用初值的 continuation 是错误的。
;; Returns the arithmetic, geometric, and
;; harmonic means of a nested list of numbers
(define (means ton)
(letrec*
((mean
(lambda (f g)
(f (/ (sum g ton) n))))
(sum
(lambda (g ton)
(if (null? ton)
(+)
(if (number? ton)
(g ton)
(+ (sum g (car ton))
(sum g (cdr ton)))))))
(n (sum (lambda (x) 1) ton)))
(values (mean values values)
(mean exp log)
(mean / /))))
计算 ( means '(3 (1 4))) 返回三个值:8/3, 2.28942848510666 (近似), 和 36/19。
let-values ((formals init) ... ) expr expr ... [标准扩展特殊形式]
初始值在当前环境中计算(以不确定的顺序),就好像调用 call-with-value 一样,并且,formals 中出现的变量被绑定到保存 init 返回值的新位置,其中, formal 与返回值的匹配方式与lambda表达式中的formal与过程调用中的参数的匹配方式相同。然后,expts 在扩展环境中计算,最后一个表达式的值最为返回值。每一个变量的绑定都以 exprs 作为其区域。
如果formals与相应init返回的值数量不匹配,则为错误。
(let-values (((root rem) (exact-integer-sqrt 32)))
(* root rem)) => 35
let*-values ((formals init) ... ) expr expr ... [ 标准扩展特殊形式 ]
let*-values 构造类似于 let-vales,但 inits 是按从左到右的顺序依次计算和创建绑定,每个formal的绑定区域包括其右侧的 init 以及主体。因此,第二个 init 在第一组绑定可见并初始化的环境中进行计算,依此类推。
(let ((a ’a) (b ’b) (x ’x) (y ’y))
(let*-values (((a b) (values x y))
((x y) (values a b)))
(list a b x y))) => (x y x y)
(这一部分,对let-value 和let*-value理解的很不到位,翻译的不好)