The Little Schemer 知识点整理(五):我得天!都是星星
2018 年 12 月 13 日
准备知识
- 
所有 * 函数所处理的列表,都满足以下情况中的一种:
- 空列表。
 - 一个原子 cons 在一个列表上。
 - 一个列表 cons 在另一个列表上。
 
 (and ...)第一个参数为 false 则直接返回 false;(or ...)第一个参数为 true 则直接返回 true。所以它们的问题可能不会全部问到。- 一个 S-表达式要么是一个原子,要么是一个 S-表达式列表(也可以是空列表)。
 - 注意区分 
eq?、=、eqan?和equal?。 
Scheme 十诫之第一诫(终极式)
- 当对一个原子列表 
lat进行递归调用时,询问两个有关lat的问题:(null? lat)和else。 - 当对一个数字 
n进行递归调用时,询问两个有关n的问题:(zero? n)和else。 - 当对一个 S-表达式列表 
l进行递归调用时,询问三个有关l的问题:null? l、(atom? (car l))和else。 
Scheme 十诫之第四诫(终极式)
在递归时总是改变至少一个参数:
- 当对一个原子列表 
lat进行递归调用时,使用(car lat)。 - 当对数字 
n进行递归调用时,使用(sub1 n)。 - 当对一个 S-表达式进行递归调用时,只要 
(null? lat)和(atom? (car l))都不为 true,那么就同时使用(car lat)和(cdr lat)。 
在递归时改变的参数,必须向着不断接近结束条件而改变。改变的参数必须在结束条件中得以测试:
- 当使用 
cdr时,用null?测试是否结束。 - 当使用 
sub1时,用zero?测试是否结束。 
Scheme 十诫之第六诫
简化工作只在功能正确之后开展。
自定义函数
(rember* a l)
定义:移除列表 l 中的全部原子 a,以此类推。
实现:
(define rember*
  (lambda (a l)
    (cond
      ((null? l) (quote ()))
      ((atom? a)
        (cond
          ((eq? (car l) a) (rember* a (cdr l)))
          (else (cons (car l)
                      (rember* a (cdr l))))))
      (else (cons (rember* a (car l))
                  (rember* a (cdr l)))))))(insertR* new old l)
定义:在列表 l 中的所有原子 old 后面插入原子 new。
实现:
(define insertR*
  (lambda (new old l)
    (cond
      ((null? l) (quote ()))
      ((atom? (car l))
        (cond
          ((eq? (car l) old)
            (cons old
                  (cons new
                        (insertR* new old (cdr l)))))
          (else (cons (car l)
                      (insertR* new old (cdr l))))))
      (else (cons (insertR* new old (car l))
                  (insertR* new old (cdr l)))))))比较 rember* 和 insertR* 两个函数,看看是怎么问问题的。
(occur* a l)
定义:统计列表 l 中原子 a 出现的所有次数。
实现:
(define occur*
  (lambda (a l)
    (cond
      ((null? l) 0)
      ((atom? (car l))
        (cond
          ((eq? (car l) a)
            (add1 (occur* a (cdr l))))
          (else (occur* a (cdr l)))))
      (else (plus (occur* a (car l))
                  (occur* a (cdr l)))))))(subst* new old l)
定义:把列表 l 中的所有原子 old 都替换为原子 new。
实现:
(define subst*
  (lambda (new old l)
    (cond
      ((null? l) (quote ()))
      ((atom? (car l))
        (cond
          ((eq? (car l) old)
            (cons new
                  (subst* new old (cdr l))))
          (else (cons old
                      (subst* new old (cdr l)))))
      (else (cons (subst* new old (car l))
                  (subst* new old (cdr l))))))))(insertL* new old l)
定义:在列表 l 中的所有原子 old 前面插入原子 new。
实现:
(define insertL*
  (lambda (new old l)
    (cond
      ((null? l) (quote ()))
      ((atom? (car l))
        (cond
          ((eq? (car l) old)
            (cons new
                  (cons old
                        (insertL* new old (cdr l)))))
          (else (cons old
                      (insertL* new old (cdr l)))))
      (else (cons (insertL* new old (car l))
                  (insertL* new old (cdr l))))))))(member* a l)
定义:判断原子 a 是否出现在列表 l 中。
实现:
(define member*
  (lambda (a l)
    (cond
      ((null? l) #f)
      ((atom? (car l))
        (or (eq? (car l) a)
            (member* a (cdr l))))
      (else (or (member* a (car l))
                (member* a (cdr l)))))))回忆一下 or 的用法。
(leftmost l)
定义:找出非空 S-表达式列表 l 中最左边的原子。同时该列表不包含空列表元素。
实现:
(define leftmost
  (lambda (l)
    (cond
      ((atom? (car l)) (car l))
      (else (leftmost a (cdr l))))))(eqlist? l1 l2)
定义:判断两个列表是否相等。
思考:需要问 9 个问题,因为处理列表有 3 种情况,两个列表就是 9 种情况。
实现:
(define eqlist?
  (lambda (l1 l2)
    (cond
      ((and (null? l1) (null? l2)) #t)
      ((and (null? l1) (atom? (car l))) #f)
      ((null? l1) #f)
      ((and (atom? (car l1)) (null? l2)) #f)
      ((and (atom? (car l1)) (atom? (car l2)))
        (and (eqan? (car l1) (car l2))
             (eqlist? (cdr l1) (cdr l2))))
      ((atom? (car l1)) #f)
      ((null? l2) #f)
      ((atom? (car l2)) #f)
      (else (and (eqlist? (car l1) (car l2))
                 (eqlist? (cdr l1) (cdr l2)))))))(and (null? l1) (null? l2)) 和 (or (null? l1) (null?l2)) 可以确定 3 种情况,以此简化一下:
(define eqlist?
  (lambda (l1 l2)
    (cond
      ((and (null? l1) (null? l2)) #t)
      ((or (null? l1) (null? l2)) #f)
      ((and (atom? (car l1)) (atom? (car l2)))
        (and (eqan? (car l1) (car l2))
             (eqlist? (cdr l1) (cdr l2))))
      ((or (atom? (car l1)) (atom? (car l2))) #f)
      (else (and (eqlist? (car l1) (car l2))
                 (eqlist? (cdr l1) (cdr l2)))))))(equal? s1 s2)
定义:判断两个 S-表达式是否相同。
实现:
(define equal?
  (lambda (s1 s2)
    (cond
      ((and (atom? s1) (atom? s2))
        (eqan? s1 s2))
      ((atom? s1) #f)
      ((atom? s2) #f)
      (else (eqlist? s1 s2)))))同上,第二第三个问题可以归纳为 (or (atom? s1) (atom? s2)),简化版:
(define equal?
  (lambda (s1 s2)
    (cond
      ((and (atom? s1) (atom? s2))
        (eqan? s1 s2))
      ((or (atom? s1) (atom? s2))
        #f)
      (else (eqlist? s1 s2)))))接下来用 equal? 重写 eqlist?:
(define eqlist?
  (lambda (l1 l2)
    (cond
      ((and (null? l1) (null? l2)) #t)
      ((or (null? l1) (null? l2)) #f)
      (else (and (equal? (car l1) (car l2))
                 (eqlist? (cdr l1) (cdr l2)))))))(rember s l)
定义:详见第三篇,这里是用任意 S-表达式列表 l 替代 lat,任意 S-表达式 s 替代原子 a 之后的版本。用到了上述的 equal?。
实现:
(define rember
  (lambda (s l)
    (cond
      ((null? l) (quote ()))
      ((equal? (car l) s) (cdr l))
      (else (cons (car l)
                  (rember s (cdr l)))))))EOF