01 Toys
atom 与 list
atom 与 list 都是 S-expr
()
被称为 null list,也是一个 list'atom
等价于(quote atom)
,'list
等价于(quote list)
02 Do It, Do It Again, and Again, and Again…
列表
(car l)
表示取出 list 的第一个元素,结果可以是 atom 也可以是 list,如(car '((a b c) x y z)) => '(a b c)
(cdr l)
表示取出 list 里除了第一个元素的剩下部分,如(cdr '((a b c) x y z)) => '(x y z)
,当 list 只有一个元素时返回()
car
与 cdr
都不可对 atom 或者 null list 操作
(cons x l)
表示把一个元素加到另一个 list 的头部,返回一个 list,如(cons 'a (car '((b) c d))) => '(a b)
,第一个参数随意,第二个参数必须是 list
可以用 (cons atom '())
构建一个表 '(atom)
(null? l)
表示判断 list 是否为 null,如(null? '(a)) => #f
,参数必须是 list
(define atom?
(lambda (x)
(and (not (pair? x))
(not (null? x)))))
(eq? a b)
表示判断两个 atom 是否相等,如(eq? 'a 'a) => #
,参数必须是非数字的 atom,不可以是 list(lat? lat)
表示判断一个 list 全部都是由 atom 组成,如(lat? '(a b (c))) => #f
,对于 null list 也返回#t
(define lat?
(lambda (l)
(cond
((null? l) #t)
((atom? (car l)) (lat? (cdr l)))
(else #f))))
本章介绍了 scheme 的基本操作,其中最为重要的是 cond 的使用。模式匹配是 scheme 强大的武器,贯穿了这本书,
也是函数式编程的重要思考方式。
03 Cons the Magnificent
简单列表函数
(member? a lat)
表示判断一个 atom 是否为一个 lat 的一部分,如(member? 'a '(a b c)) => #t
(define member?
(lambda (a lat)
(cond
((null? lat) #f)
((eq? a (car lat)) #t)
(else (member? a (cdr lat))))))
(rember a lat)
表示从一个 lat 中移除一个 atom 第一次出现的地方,如(rember 'mint '(mint lamb chops mint)) => '(lamb chops)
,若未出现则不改变
(define rember
(lambda (a lat)
(cond
((null? lat) '())
((eq? a (car lat)) (cdr lat))
(else (cons (car lat)
(rember a (cdr lat)))))))
(firsts l)
表示取出 list 中每一个子列表的第一个元素,来组成新列表,如(firsts '((a b) (c d) (e f))) => '(a c e)
(define firsts
(lambda (l)
(cond
((null? l) '())
(else (cons (car (car l)) (firsts (cdr l)))))))
(insertR a b lat)
表示把 atom a 插入到 lat 里 atom b 第一次出现的位置后,如(insertR 'a 'b '(c d b)) => '(c d b a)
(define insertR
(lambda (a b lat)
(cond
((null? lat) '())
((eq? b (car lat)) (cons b
(cons a (cdr lat))))
(else (cons (car lat)
(insertR a b (cdr lat)))))))
(define insertL
(lambda (a b lat)
(cond
((null? lat) '())
((eq? b (car lat)) (cons a lat))
(else (cons (car lat)
(insertL a b (cdr lat)))))))
(subst a b lat)
表示用 atom a 替换第一个 atom b,如(subst 'a 'b '(c b)) => '(c a)
(define subst
(lambda (a b lat)
(cond
((null? lat) '())
((eq? b (car lat)) (cons a (cdr lat)))
(else (cons (car lat)
(subst a b (cdr lat)))))))
(subst2 a b c lat)
表示用 atom a 替换 atom b 或第一个 atom c ,如(subst2 'a 'b '(c b)) => '(c a)
(define subst2
(lambda (a b c lat)
(cond
((null? lat) '())
((or (eq? b (car lat))
(eq? c (car lat))) (cons a (cdr lat)))
(else (cons (car lat)
(subst2 a b c (cdr lat)))))))
多次操作的列表函数
(multirember a lat)
表示删除 lat 中所有的 atom a
(define multirember
(lambda (a lat)
(cond
((null? lat) '())
((eq? a (car lat)) (multirember a (cdr lat)))
(else (cons (car lat)
(multirember a (cdr lat)))))))
(multiinsertR a b lat)
表示在所有的 atom b 后面插入 atom a
(define multiinsertR
(lambda (a b lat)
(cond
((null? lat) '())
((eq? b (car lat)) (cons b
(cons a (multiinsertR a b (cdr lat)))))
(else (cons (car lat)
(multiinsertR a b (cdr lat)))))))
(multiinsertR a b lat)
表示在所有的 atom b 前面插入 atom a
(define multiinsertL
(lambda (a b lat)
(cond
((null? lat) '())
((eq? b (car lat)) (cons a (cons b (multiinsertL a b (cdr lat))))) ; 注意这个地方
(else (cons (car lat)
(multiinsertL a b (cdr lat)))))))
(multisubst a b lat)
表示把所有的 atom b 替换成 atom a
(define multisubst
(lambda (a b lat)
(cond
((null? lat) '())
((eq? b (car lat)) (cons a (multisubst a b (cdr lat))))
(else (cons (car lat)
(multisubst a b (cdr lat)))))))
04 Number Games
普通数字操作
下面只考虑非负整数。
(add1 x)
返回 x+1(sub1 x)
返回 x-1(zero? x)
判断 x 是否为 0(o+ m n)
返回 m+n(o- m n)
返回 m-n
(define o+
(lambda (m n)
(cond
((zero? m) n)
(else (add1 (o+ n (sub1 m)))))))
(define o-
(lambda (m n)
(cond
((zero? m) n)
(else (sub1 (o- n (sub1 m)))))))
可以发现数字操作可以与列表操作对应,zero?
对应 null?
,add1
对应 cons
- tup 表示一个纯数字构成的 list,如
'(1 2 3)
,()
称作 null tup
下面尝试将列表操作对应到数字操作
tup 的判空语句和 list 一样,都是 (null? tup)
。同时,在数字运算中,((null? l) '())
变为了 ((null? tup) 0)
(addtup tup)
求出 tup 中所有数字之和
(define addtup
(lambda (tup)
(cond
((null? tup) 0)
(else (o+ (car tup)
(addtup (cdr tup)))))))
(o* a b)
返回 a*b
(define o*
(lambda (a b)
(cond
((zero? b) 0)
(else (o+ a (o* a (sub1 b)))))))
(tup+ tup1 tup2)
将 tup1 中元素与 tup2 中对应位置的元素相加,对于空元素视为 0,如(tup+ '(1 2 3) '(3 4)) => '(4 6 3)
(define tup+
(lambda (tup1 tup2)
(cond
((and (null? tup1)
(null? tup2)) '())
((null? tup1) tup2)
((null? tup2) tup1)
(else (cons (o+ (car tup1) (car tup2))
(tup+ (cdr tup1) (cdr tup2)))))))
(> a b)
返回 a>b
(define >
(lambda (a b)
(cond
(and (zero? a)
(zero? b) #f)
((zero? a) #f)
((zero? b) #t)
(else (> (sub1 a) (sub1 b))))))
(
a b)= 返回a=b
(define =
(lambda (a b)
(cond
((> a b) #f)
((< a b) #f)
(else #t))))
=
等价于 eq?
(^ a b)
计算a^b
(define ^
(lambda (a b)
(cond
((zero? b) 1)
(else (o* a (^ a (sub1 b)))))))
(o/ a b)
计算 a/b
(define o/
(lambda (a b)
(cond
((zero? a) 0)
(else (add1 (o/ (o- a b) b))))))
列表中的数字运算
(length lat)
计算 lat 的长度
(define length
(lambda (lat)
(cond
((null? lat) 0)
(else (add1 (length (cdr lat)))))))
(pick n lat)
返回计算 lat 中第 n 个元素
(define pick
(lambda (n lat)
(cond
((zero? (sub1 n)) (car lat))
(else (pick (sub1 n) (cdr lat))))))
(rempick n lat)
返回去掉第 n 个元素后的 lat
(define rempick
(lambda (n lat)
(cond
((zero? (sub1 n)) (cdr lat)) ; 注意
(else (cons (car lat)
(rempick (sub1 n) (cdr lat)))))))
(number? n)
判断 n 是否为数字,注意这个函数是基础函数,无法表达(non-nums lat)
去除 lat 中所有的数字
(define non-nums
(lambda (lat)
(cond
((null? lat) '())
((number? (car lat)) (non-nums (cdr lat)))
(else (cons (car lat)
(non-nums (cdr lat)))))))
(all-nums lat)
提取出 lat 中所有数字组成 tup
(define all-nums
(lambda (lat)
(cond
((null? lat) '())
((number? (car lat)) (cons (car lat)
(all-nums (cdr lat))))
(else (all-nums (cdr lat))))))
(eqan? a b)
比较 a 和 b 是否相同(考虑数字和普通 atom)
(define eqan?
(lambda (a b)
(cond
((and (number? a)
(number? b) (= a b)))
((or (number? a)
(number? b)) #f)
(else (eq? a b)))))
(occur a lat)
统计 a 在 lat 中出现的次数
(define occur
(lambda (a lat)
(cond
((null? lat) 0)
((eq? (car lat) a) (add1 (occur a (cdr lat))))
(else (occur a (cdr lat))))))