第三贴: 语句块
读者也许已经注意到: 前面的 scheme 例子中列举了许多符号表达式, 其实在 scheme 中, 程序就是数据. 因此, #\c
就是一个程序,或者语句块.
scheme 对语句块 #\c
求值为 #\c
, 因为 #\c
是自求值的. 并不是所有符号表达式都是自求值的. 比如, 表达式 xyz
求值结果为 xyz
变量所绑定的值. 而列表符号表达式 (string->number "16")
求值结果是数字 16
.
并不是所有的符号表达是都是正确程序. 如果你写如下的代码 (1 . 2)
, scheme 就会报错.
scheme 对一个列表符号表达式求值, 首先求值语句块的第一个元素, 如果其为一个过程, 那么语句块剩下的元素会被求值以作为这个过程的参数; 如果第一个元素是一种特殊语句, 那么就会以一种特殊的方式来对该语句进行求值, 我们前面碰到的这种特殊语句有 begin
, define
和 set!
. begin
语句对其后的语句进行顺序求值, 并返回最后一个语句的求值结果. define
语句定义变量并将一个值绑定到该变量, set!
改变变量绑定的值.
Procedures(过程)
我们已经看过许多 scheme 内置过程, 例如: cons
, string->list
等等.
我们也可以用 lambda
语句来自定义过程. 比如, 下面定义了一个程序, 对其参数加 2
:
guile> (lambda (x) (+ x 2))
其中, 第一个子语句 (x)
是参数类表, 剩下的子语句是过程体. 这个函数可以向内置过程那样对一个参数进行调用:
guile> ((lambda (x) (+ x 2)) 5)
7
如果以后会调用这个过程很多次, 我们可以把这个过程绑定到一个变量, 以后直接引用变量就可以了:
guile> (define add2
... (lambda (x) (+ x 2)))
guile> (add2 5)
7
参数列表
一个 lambda-过程的参数列表就是紧跟着 lambda 符号的第一个子语句. add2
是一元参数过程, 它的参数列表为 (x)
, 在过程体内, 符号 x
就是被调用过程的参数所绑定到的变量, 变量 x
是过程体中的局部变量.
我们可以定义二元或多元参数过程. 下面定义了一个二元参数过程来计算矩形的面积, 此函数的两个参数分别是矩形的长和宽:
guile> (define area
... (lambda (length breadth)
... (* length breadth)))
guile> (area 3 2)
6
注意: area
过程对其两参数进行相乘, 同内置函数 *
一样. 因此, 我们可以更简单定义 area
过程:
guile> (define area *)
guile> (area 4 5)
20
变参
某些过程支持可变数量的参数, 这个只需把 lambda 参数列表换作一个符号symbol
, 这个符号作为该过程的一个变量被绑定到所调用过程的参数组成的列表.
通常, lambda 参数列表可以为一个形如 (x ...)
的列表, 或者是一个形如 (x ... . z)
的点对.
当是点对的时候, 在点.
之前的所有变量被绑定到被调过程相应的参数,
点.
之后的那个变量被绑定到被调过程剩下的所有参数所组成的列表.
Apply
scheme 中的 apply
过程可以把一个过程作用到一个参数列表:
guile> (define x '(1 2 3))
guile> (apply + x)
6
通常, apply
过程第一个参数为一个过程, 其后参数的数量是可变的, 但是, 最后一个参数必须为一个列表:
guile> (apply + 1 2 3 x)
12
Sequencing
我们可以用 begin
特殊语句把那些需要顺序执行的子语句组合起来. 许多 scheme 语句含有隐式的 begin
语句.
比如, 我们可以定义一个过程用来显示它的三个参数, 其间用空格分离, 下面就是一种定义方法:
(define display3
(lambda (arg1 arg2 arg3)
(begin
(display arg1)
(display " ")
(display arg2)
(display " ")
(display arg3)
(newline))))
在 scheme 中, lambda
语句体就是一个隐式的 begin
语句,
因此, 在 display3
的定义中, begin
是可以省略的,
更简单的一种方法是下面的定义:
(define display3
(lambda (arg1 arg2 arg3)
(display arg1)
(display " ")
(display arg2)
(display " ")
(display arg3)
(newline)))