《The Little Schemer》知识点整理(七):朋友及关系

准备知识

  1. set:集合(注意区别于 collection)。
  2. pair:其中的两个元素是含义不同但是相关的对象。
  3. 构建 pair:(cons x1 (cons x2 (quote ())))
  4. 构建 pair 的表示方式:

    1
    2
    3
    (define first
    (lambda (p)
    (car p)))
    1
    2
    3
    (define second
    (lambda (p)
    (car (cdr p))))
    1
    2
    3
    (define build
    (lambda (s1 s2)
    (cons s1 (cons s2 (quote ())))))
  5. rel:由 pair 组成的 set。

自定义函数

(set? lat)

定义:判断一个 lat 是不是一个集合(set),集合里不含相同原子。

实现:

1
2
3
4
5
6
(define set?
(lambda (lat)
(cond
((null? lat) #t)
((member? (car lat) (cdr lat)) #f)
(else (set? (cdr lat))))))

注意 member?,第二章就已经实现了这个函数。

(makeset lat)

定义:把一个 lat 转化为 set(原子去重)。

实现:

1
2
3
4
5
6
7
8
9
(define makeset
(lambda (lat)
(cond
((null? lat) (quote ()))
((member? (car lat) (cdr lat))
(makeset (cdr lat)))
(else
(cons (car lat)
(makeset (cdr lat)))))))

我们使用 multirember 函数重写 makeset

1
2
3
4
5
6
7
8
9
(define makeset
(lambda (lat)
(cond
((null? lat) (quote ()))
(else
(cons (car lat)
(makeset
(multirember (car lat)
(cdr lat))))))))

与第一次写出的函数不同的是,这次我们首先移除了剩余列表中的重复原子,最终的打印结果也更加符合直觉。

(subset? set1 set2)

定义:判断 set1 是不是 set2 的子集。

实现:

1
2
3
4
5
6
7
(define subset?
(lambda (set1 set2)
(cond
((null? set1) #t)
((member? (car set1) set2)
(subset? (cdr set1) set2))
(else #f))))

改用 and

1
2
3
4
5
6
7
(define subset?
(lambda (set1 set2)
(cond
((null? set1) #t)
(else
(and (member? (car set1) set2)
(subset? (cdr set1) set2))))))

(eqset? set1 set2)

定义:判断 set1 和 set2 是否相同。

实现:

1
2
3
4
5
6
(define eqset?
(lambda (set1 set2)
(cond
((subset? set1 set2)
(subset? set2 set1))
(else #f))))

and 简化:

1
2
3
4
(define eqset?
(lambda (set1 set2)
(and (subset? set1 set2)
(subset? set2 set1))))

(intersect? set1 set2)

定义:判断 set1 中是否至少有一个原子也在 set2 中。

实现:

1
2
3
4
5
6
(define intersect?
(lambda (set1 set2)
(cond
((null? set1) #f)
((member? (car set1) set2) #t)
(else (intersect? (cdr set1) set2)))))

改用 or

1
2
3
4
5
6
7
(define intersect?
(lambda (set1 set2)
(cond
((null? set1) #f)
(else
(or (member? (car set1) set2)
(intersect? (cdr set1) set2))))))

对比一下 subset?intersect?

(intersect set1 set2)

定义:从 set2 中取出 set1 中包含的原子的列表。

实现:

1
2
3
4
5
6
7
8
(define intersect
(lambda (set1 set2)
(cond
((null? set1) (quote ()))
((member? (car set1) set2)
(cons (car set1)
(intersect (cdr set1) set2)))
(else (intersect (cdr set1) set2)))))

(union set1 set2)

定义:merge 两个 set。(不知道怎么表述了 😂)

实现:

1
2
3
4
5
6
7
8
(define union
(lambda (set1 set2)
(cond
((null? set1) set2)
((member? (car set1) set2)
(union (cdr set1) set2))
(else (cons (car set1)
(union (cdr set1) set2))))))

只改动一个条件 ((null? set1) (quote ())),这个函数回变成什么样呢?

1
2
3
4
5
6
7
8
(define xxx
(lambda (set1 set2)
(cond
((null? set1) (quote ()))
((member? (car set1) set2)
(xxx (cdr set1) set2))
(else (cons (car set1)
(xxx (cdr set1) set2))))))

xxx 函数返回只存在于 set1 却不存在于 set2 中的原子列表。

(intersectall l-set)

定义:l-set 是由一组列表组成的非空列表,该函数取出这些列表中的相同原子,返回这些原子组成的新列表。

实现:

1
2
3
4
5
6
(define intersectall
(lambda (l-set)
(cond
((null? (car l-set)) (car l-set))
(else (intersect (car l-set)
(intersectall (cdr l-set)))))))

(a-pair? x)

定义:判断 S-表达式 x 是不是一个 pair(仅有两个 S-表达式组成的列表)。

实现:

1
2
3
4
5
6
7
8
(define a-pair
(lambda (x)
(cond
((atom? x) #f)
((null? x) #f)
((null? (cdr x)) #f)
((null? (cdr (cdr x))) #t)
(else #f))))

无知的分割线

本章接下来的一部分有点没看懂,不是很明白这里介绍的函数和全函数的概念。所以只写下自己所理解的。

(fun? rel)

定义:判断一个 rel 中,任一 pair 的第一个元素都与其他 pair 的第一个元素是否相同。或者可以表述为,判断一个 rel 中由每个 pair 的第一个元素组成的新列表是否为一个 set。我们把这样的 rel 称之为函数(?)。

实现:

1
2
3
(define fun?
(lambda (rel)
(set? (firsts rel))))

firsts 的定义可以回顾一下第三章。

(revrel rel)

定义:交换一个 rel 中每个 pair 的两个元素。

实现:

1
2
3
4
5
6
7
8
(define revrel
(lambda (rel)
(cond
((null? rel) (quote ()))
(else (cons
(build (first (car rel))
(second (car rel)))
(revrel (cdr rel)))))))

(revpair pair)

定义:交换一个 pair 中的两个元素。

实现:

1
2
3
(define revpair
(lambda (pair)
(build (second pair) (first pair))))

那么 revrel 函数就可以表述为以下形式,大大提升了可读性:

1
2
3
4
5
6
7
(define revrel
(lambda (rel)
(cond
((null? rel) (quote ()))
(else (cons
(revpair (car rel))
(revrel (cdr rel)))))))

(fullfun? fun)

定义:一个 rel 除了是 fun 以外,它的每个 pair 的第二元素也与其他 pair 的第二个元素不同。我们把这样的 fun 称之为全函数(?)。它是一一对应的。

实现:

1
2
3
(define fullfun?
(lambda (fun)
(set? (seconds fun))))

我们可以像实现 firsts 那样实现 seconds 函数。当然也可以用上面已经引入的函数:

1
2
3
(define fullfun?
(lambda (fun)
(fun? (revrel fun))))

🐱 写到现在觉得书比自己整理的好看太多了,这本书一点也不枯燥 🥰

– EOF –