5.2 语境 (Context)
另一个我们用来区分表达式的操作符是 let
。它接受一个代码主体,但允许我们在主体内设置新变量:
> (let ((x 7)
(y 2))
(format t "Number")
(+ x y))
Number
9
一个像是 let
的操作符,创造出一个新的词法语境(lexical context)。在这个语境里有两个新变量,然而在外部语境的变量也因此变得不可视了。
概念上说,一个 let
表达式等同于函数调用。在 2.14 节证明过,函数可以用名字来引用,也可以通过使用一个 lambda 表达式从字面上来引用。由于 lambda 表达式是函数的名字,我们可以像使用函数名那样,把 lambda 表达式作为函数调用的第一个实参:
> ((lambda (x) (+ x 1)) 3)
4
前述的 let
表达式,实际上等同于:
((lambda (x y)
(format t "Number")
(+ x y))
7
2)
如果有关于 let
的任何问题,应该是如何把责任交给 lambda
,因为进入一个 let
等同于执行一个函数调用。
这个模型清楚的告诉我们,由 let
创造的变量的值,不能依赖其它由同一个 let
所创造的变量。举例来说,如果我们试着:
(let ((x 2)
(y (+ x 1)))
(+ x y))
在 (+ x 1)
中的 x
不是前一行所设置的值,因为整个表达式等同于:
((lambda (x y) (+ x y)) 2
(+ x 1))
这里明显看到 (+ x 1)
作为实参传给函数,不能引用函数内的形参 x
。
所以如果你真的想要新变量的值,依赖同一个表达式所设立的另一个变量?在这个情况下,使用一个变形版本 let*
:
> (let* ((x 1)
(y (+ x 1)))
(+ x y))
3
一个 let*
功能上等同于一系列嵌套的 let
。这个特别的例子等同于:
(let ((x 1))
(let ((y (+ x 1)))
(+ x y)))
let
与 let*
将变量初始值都设为 nil
。nil
为初始值的变量,不需要依附在列表内:
> (let (x y)
(list x y))
(NIL NIL)
destructuring-bind
宏是通用化的 let
。其接受单一变量,一个模式 (pattern) ── 一个或多个变量所构成的树 ── 并将它们与某个实际的树所对应的部份做绑定。举例来说:
> (destructuring-bind (w (x y) . z) '(a (b c) d e)
(list w x y z))
(A B C (D E))
若给定的树(第二个实参)没有与模式匹配(第一个参数)时,会产生错误。