Ch2 特殊形式
特殊形式是具有特殊求值规则的表达式。本章将讲述 Scheme 中的基本特殊形式。
Sec 2.1 Lambda 表达式
lambda formals expr expr ... [ extended standard special form ]
lambda表达式的计算结果为过程。计算 lambda 表达式时的有效环境被作为过程的一部分被记住。它被称为封闭环境。当随后使用一些参数调用该过程时,封闭环境将通过与形式参数中的变量绑定而扩展到新位置,同时,这些参数根据对应规则填充到相应的位置。这个过程创建的新环境称为调用环境。这个过程创建的新环境称为调用环境。
一旦调用环境被构建,lambda 体中的表达式将在其中按顺序计算。这意味着被 lambda 表达式绑定的变量区域是主体中的所有表达式。计算结果是主体中的最后一个表达式,它的值将作为过程调用的结果返回。
Formals, 即形式参数列表,通常被称为 lambda 列表。
将形式参数与参数匹配的过程有些复杂。有三种类型的参数,匹配将按顺序处理每种参数:
Required 首先将所有的必须参数与参数进行匹配。如果所给参数少于要求的必须参数,
则会出现错误信息,类型为:condition-type:wrong-number-of-arguments;
如果所给参数多于必须参数,而此处又没有其他类型参数,则也会发出此错
误信号。
Optional 一旦必须参数全部匹配完成,可选参数将和剩余参数进行对应匹配。如果剩
余的参数比可选参数少,没有被匹配的可选参数将与被称为默认对象的特殊
对象绑定。如果剩余参数比可选参数多,且此处没有其他类型的参数了,则
会发出错误信号,类型为:condition-type:wrong-number-of-arguments。
谓词 default-object?仅适用于默认对象,可用于确定哪些参数是提供了值
的,哪些是默认参数值。
Rest 最后,如果有一个 rest 参数(只能有一个),则所有剩余的参数都将被放入
一个列表中,并且该列表将绑定到 rest 参数。(如果没有剩余参数,则 rest
参数与空列表绑定。)不像其他 Lisp 的实现,在 Scheme 中,rest 参数绑定
的列表总是新分配的。它有无限的范围,可以在不影响过程调用的情况下进
行修改。
专门的识别关键字把形式参数分为以上三类。这里所用的关键字为 ' #!optional ',' . ' 和 ' #!rest '。请注意, 只用 '.' 是在标准 Scheme 里定义的, 其它两个是 MIT/GNU Scheme 扩展的。'#!rest' 和 ' . ' 在形式上具有相同意义。
这些关键字的使用最好通过例子来解释。下面是典型的 lambda 列表,后面是关于哪些参数是必须的、可选的和剩余的描述。我们将在这些例子中使用' #!rest ' ,但在它出现的任何地方都可以使用 ' . ' 替换。
(a b c) a,b 和 c 都是必须参数。过程必须正好传递这三个参数。
(a b #!optional c) a,b 是必须参数,c 是可选参数。过程可以传递两个或三个参数。
(#!optional a b c) a,b 和 c 都是可选参数。过程可以传递 0 -- 3 之间任意数量的参数。
a
(#!rest a) 这两个例子等价。 a 是一个 rest 参数。过程可以传递任意多数量的参
数。注意:这是唯一一种不能用 ' . ' 替代 ' #!rest ' 的情况。(这个情况
指的是过程只有一个形式参数,且这个形式参数是 Rest 型。)
(a b #!optional c d #!rest e)
a 和 b 是必须参数,c 和 d 是可选参数,e 是 rest 参数。过程可以传
递两个或更多参数。
一些 lambda 表达式的例子:
(lambda (x) (+ x x)) => #[compound-procedure 53]
((lambda (x) (+ x x)) 4) => 8
(define reverse-subtract
(lambda (x y)
(- y x)))
(reverse-subtract 7 10) => 3
(define foo
(let ((x 4))
(lambda (y) (+ x y))))
(foo 6) => 10
named-lambda formals expression expression ... [special form]
特殊形式 named-lambda 与 lambda 相似,除了参数列表中的第一个必须参数不是参数,而是结果过程的名字;也就是说,必须至少含有一个参数。这个名字没有语义含义,但是包含在过程的外部表示中,因此在调试中非常有用。在 MIT/GNU Scheme 中,lambda 就像 named-lambda 的无命名方式一样运行。
(named-lambda (f x) (+ x x)) => #[compound-procedure 53 f]
(named-lambda (f x) (+ x x)) 4) => 8