表达式类型分为基本类型和派生类型两种.
- 基本表达式类型包括变量和过程调用.
- 派生表达式类型在语义上不是基本类型,但可以被定义为宏.
基本表达式类型
变量引用
由一个变量构成的表达式是变量引用.
(define x 28)
x => 28
常量表达式
(quote <datum>) 可简写为 '<datum>
数值常量,字符常量,字符串常量和布尔常量的值是”它们自身”.
<datum>
可以是Scheme对象的任何外部表示.
(quote a) => a
(quote #(a b c)) => #(a b c)
(quote (+ 1 2)) => (+ 1 2)
'"abc" => "abc"
"abc" => "abc"
'145932 => 145932
145932 => 145932
'#t => #t
过程调用
(<operator> <
operand
1
>...)
简单地用括号表达式括起被调用过程及传入其中的参数即构成了过程调用.
过程调用也被称为组合式.
(+ 3 4) => 7
((if #f + *) 3 4) => 28
过程
(lambda <formals> <body>)
<formals>
为形参列表
<body>
为一个或多个表达式组成的序列
lambda表达式的值是一个过程.
(lambda (x) (+ x x)) => 一个过程
((lambda (x) (+ x x)) 4) => 8
(define reverse-subtract
(lambda (x y) (- y x)))
(reverse-subtract 7 10) => 3
((lambda x x) 3 4 5 6) => (3 4 5 6)
((lambda (x y . z) z)
3 4 5 6) => (5 6)
条件表达式
(if <test> <consequent> <alternate>)
(if <test> <consequent>)
(if (> 3 2) 'yes 'no) => yes
(if (> 3 2) (- 3 2)
(+ 3 2)) => 5
赋值
(set! <variable> <expression>)
(define x 2)
(+ x 1) => 3
(set! x 4) => 未定义
(+ x 1) => 5
派生表达式类型
条件表达式
(cond <
clause
1
> <
clause
2
>...)
<clause>
的格式为:
(<test> <
expression
1
>...) 或 (<test> => <expression>)
※<test>
是任意表达式。
最后一个<clause>
可以是一个”else子句”:
(else <
expression
1
> <
expression
2
>...)
(case <key> <
clause
1
> <
clause
2
>...)
<key>
是任意表达式。
<clause>
的格式为:
((<
datum
1
>...) <
clause
1
> <
clause
2
>...)
※<datum>
是某个对象的外部表示
最后一个〈clause〉可以是一个”else子句”:
(else <
expression
1
> <
expression
2
>...)
(and <
test
1
>...)
<test>
表达式被从左到右求值,第一个结果为假的表达式的值被返回,剩下的所有表达式均不参与求值。如果所有表达式的值均为真,则返回最后一个表达式的值。如果没有表达式,则返回#t。
(and) => #t
**(or <
test
1
>...)
<test>
表达式被从左到右求值,第一个结果为真的表达式的值被返回,剩下的所有表达式均不参与求值。如果所有表达式的值均为假,则返回最后一个表达式的值。如果没有表达式,则返回#f。
(or) => #f
绑定结构
三种绑定结构let、let*和letrec为Scheme提供了像Algol60那样的块结构。三者的语法形式相同,但它们为各自的变量绑定所创建的作用域是不同的。
(let <bindings> <body>)
<bindings>
的格式为:
((<
variable
1
> <
init
1
>)...)
在当前环境中,所有以某种顺序求值,所有被绑定到存储了的结果的新存储位置。在扩展后的环境中求值,中最后一个表达式的(一个或多个)结果被返回。每个的绑定都将当作自己的作用域。
(let ((x 2) (y 3))
(let ((x 7)
(z (+ x y)))
(* z x))) => 35
(let* <bindings> <body>)
<bindings>
的格式为:
((<
variable
1
> <
init
1
>)...)
let*和let相似,但绑定是按照从左到右的顺序完成的,( )所指明的绑定的作用域是let* 表达式中该绑定右边的部分。因此,第二个绑定是在第一个绑定可见的环境中完成的,依此类推。
(let ((x 2) (y 3))
(let ((x 7)
(z (+ x y)))
(* z x))) => 70
(letrec <bindings> <body>)
<bindings>
的格式为:
((<
variable
1
> <
init
1
>)...)
所有〈variable〉的绑定都把整个letrec表达式当作自己的作用域,这使得定义相互递归的过程成为可能。
(letrec ((even?
(lambda (n)
(if (zero? n) #t (odd? (- n 1)))))
(odd?
(lambda (n)
(if (zero? n) #f (even? (- n 1))))))
(even? 88)) => #t
顺序结构
(begin <
expression
1
> <
expression
2
>... )
这种表达式类型用于顺序执行有”副作用”的行为,如输入和输出。
(define x 0)
(begin (set! x 5)
(+ x 1)) => 6
迭代
(do ((<
variable
1
> <
init
1
> <
step
1
>)
...)
(<test> <expression> ...)
<command> ...)
(do ((vec (make-vector 5))
(i 0 (+ i 1)))
((= i 5) vec)
(vector-set! vec i i)) => #(0 1 2 3 4)
(let ((x '(1 3 5 7 9)))
(do ((x x (cdr x))
(sum 0 (+ sum (car x))))
((null? x) sum))) => 25
(let <variable> <bindings> <body>)
“命名的let”是let语法的变形,它提供了一种比do更有普遍性的循环结构,也可用于表示递归。
(let loop ((numbers '(3 -2 1 6 -5))
(nonneg '())
(neg '()))
(cond ((null? numbers) (list nonneg neg))
((>= (car numbers) 0)
(loop (cdr numbers)
(cons (car numbers) nonneg)
neg))
((< (car numbers) 0)
(loop (cdr numbers)
nonneg
(cons (car numbers) neg)))))
=> ((6 1 3) (-5 -2))
推迟求值
(delay <expression>)
准引用
(quasiquote <qq template>) ;;qq quasiquote的略称
`<qq template>
如果<qq template>
中出现了逗号,逗号后的表达式就会被求值(“解除引用”),其结果代替逗号和表达式被插入到结构中。如果逗号后面紧跟着一个@符号,后面的表达式就被视为一个表,该表的前后括号随即被”剥离”,表中的元素被插入到,@及其后的表达式所在的位置。
`(list ,(+ 1 2) 4)
=> (list 3 4)
(let ((name 'a)) `(list ,name ',name))
=> (list a (quote a))
`(a ,(+ 1 2) ,@(map abs '(4 -5 6)) b)
=> (a 3 4 5 6 b)
`(( foo ,(- 10 3)) ,@(cdr '(c)) . ,(car '(cons)))
=> ((foo 7) . cons)
`#(10 5 ,(sqrt 4) ,@(map sqrt '(16 9)) 8)
=> #(10 5 2 4 3 8)
置换操作只用于那些与最外层反引用有同样嵌套级别的解除引用的元素。每进一个后续的准引用,嵌套级别就增加一,每进一个解除引用的语法单元,嵌套就减少一。
`(a `(b ,(+ 1 2) ,(foo ,(+ 1 3) d) e) f)
一 二 二 二 一 ;; 层数
=> (a `(b ,(+ 1 2) ,(foo 4 d) e) f)
(let ((name1 'x)
(name2 'y))
`(a `(b ,,name1 ,',name2 d) e))
一 二 二一 二一 ;; 层数
=> (a `(b ,x ,'y d) e)
宏
(<keyword> <datum> ...)
<keyword>
是能唯一确定该表达式类型的标识符,简称为关键字。
有关如何将宏的使用转译为更基本的表达式的一组规则被称为宏的转换器。
宏定义机制包含两个部分
- 一组表达式,用于设定哪些标识符是宏关键字,将他们与宏转换器相关联,并控制宏定义所在的作用域。
- 一种定义宏转换器的模式语言。
语法关键字的绑定结构
Let-syntax和letrec-syntax与let和letrec类似,但他们不是将变量绑定到存储值的位置,而是将语法关键字绑定到宏转换器(Transformer)。
(let-syntax <bindings> <body>)
<bindings>
的格式为:
((<keyword> <transformer spec>)...)
每个<keyword>
的绑定都将<body>
当作自己的作用域。
(let-syntax ((when (syntax-rules ()
((when test stmt1 stmt2 ...) (if test (begin stmt1 stmt2 ...))))))
(let ((if #t)) ;; if 被绑定到#t的存储位置
(when if (set! if 'now)) ;; 条件为真,if 被赋值为now
if)) => now
(let ((x 'outer))
(let-syntax ((m (syntax-rules () ((m) x))))
(let ((x 'inner))
(m)))) => outer
(letrec-syntax <bindings> <body>)
(letrec-syntax
((my-or (syntax-rules () ;; bindings部`
((my-or) #f)
((my-or e) e)
((my-or e1 e2 ...) (let ((temp e1)) (if temp temp (my-or e2 ...)))))))
(let ((x #f) ;; body部
(y 7)
(temp 8)
(let odd?)
(if even?))
(my-or x
(let temp)
(if y)
y))) => 7
模式语言
<transformer spec>
的格式为:
(syntax-rules <literals> <syntax rule> ... )
<literals>
是由标识符组成的表,每个<syntax rule>
的格式为:
(<pattern> <template>)