本答案部分参考SICP 解题集by huangz,和SICP学习笔记和习题解答by Kelvin。
2.2.2 层次性结构
计算叶节点
用这个递归方程计算就好了:
(leaves tree) = (leaves (car tree)) + (leaves (cdr tree)),
(leaves leave) = 1;
; 计算tree中叶节点数量
(define (leaves tree) (cond ((null? tree) 0) ((not (pair? tree)) 1) (else (+ (leaves (car tree)) (leaves (cdr tree))))))
; 计算叶节点的测试代码
(define x (cons (list 1 2) (list 3 4))) ; ((1 2) 3 4)) (length x) ; 3 (leaves x) ; 4 (list x x) ; (((1 2) 3 4) ((1 2) 3 4)) (length (list x x)) ; 2 (leaves (list x x)) ; 8
Q 2.27
题目:给出过程定义deep-reverse, 他以一个表为参数, 返回另一个表为值。结果表中的元素反转, 并且子树也反转。
思路:非常精妙的一个例程。对所有根节点利用map进行反转即可。
(define (deep-reverse tree)
(if (pair? tree)
(reverse (map deep-reverse tree))
tree))
Q 2.28
题目:给出过程定义fringe, 以一个树为参数, 返回一个由这棵树中所有叶节点组成的表, 从左到右排列。
思路:用到了一个全局变量记录结果,遍历树,将所有叶节点append到结果上。新加了一个叶节点判断的小例程 (define (leaf? item) (not (pair? item))) 。
(define (fringe tree)
(define (iter tree result)
(cond ((null? tree) result) ; 当前结点为空时,返回result
((leaf? tree) (append result (list tree))) ; 当前节点为叶时,封装为list,append至结果
(else (iter (cdr tree) (iter (car tree) result))))) ; 当前节点为根结点时,将右结点append到左结点上
(iter tree null))
Q 2.29
懒。。以后补上。。
对树的映射
对数的映射相当于map之于序列的递归版。如下两个例程返回一棵相同的缩放后的树。
; 树的映射的一个版本 (define (scale-tree tree factor) (cond ((null? tree) null) ((leaf? tree) (* tree factor)) (else (cons (scale-tree (car tree) factor) (scale-tree (cdr tree) factor))))) ; 另外一个版本,利用map操作对树映射 (define (scale-tree tree factor) (map (lambda (sub-tree) (if (pair? sub-tree) (scale-tree sub-tree factor) (* sub-tree factor))) tree))
Q 2.30
题目:分别使用/不使用map操作对树映射,返回square后的树。
思路:把之前那个版本替换下操作符就行了,没难度。
Q 2.31
题目:对上题例程进一步抽象,做出一个过程能以下面的形式定义 square-tree : (define (square-tree tree) (tree-map square tree)) 。其实还是换汤不换药的东西。
(define (tree-map func tree) (map (lambda (subtree) (if (pair? subtree) (tree-map subtree) (func subtree))) tree))
Q 2.32
题目:完成下面的过程定义,生成一个集合的所有子集的集合,并解释为何能工作。
思路:集合S的子集是(cdr S)的子集 + (cdr S)里每个元素append至(car S)。
(define (subsets s) (if (null? s) (list null) (let ((rest (subsets (cdr s)))) (append rest (map <??> rest)))))
(define (subsets s) (if (null? s) (list null) (let ((rest (subsets (cdr s)))) (append rest ; 集合S的剩余子集rest (map (lambda (x) (cons (car s) x)) rest))))) ; 加上rest里每个元素append至头元素
这题大概的递归过程就是这样的:
Sbusets(1 2 3) = Subsets(2 3) + Subsets(2 3) appending to (1) = Subsets(3) + Subsets(3) appending to (2) + Subsets(2 3) appending to (1) = Subsets() + Subsets() appending to (3) + Subsets(3) appending to (2) + Subsets(2 3) appending to (1) = () + () appending to (3) + Subsets(3) appending to (2) + Subsets(2 3) appending to (1) = () + (3) + ( () (3) ) appending to (2) + Subsets(2 3) appending to (1) = () + (3) + (2) + (2 3) + ( () + (3) + (2) + (2 3) ) appending to (1) = () + (3) + (2) + (2 3) + ( (1) + (1 3) + (1 2) + (1 2 3) ) = ( () (1) (2) (3) (1 2) (1 3) (2 3) (1 2 3) )
2.2.3 序列作为一种约定的界面
首先来看两个例程:
; 计算值为奇数的叶节点平方和 (define (sum-odd-squares tree) (cond ((null? tree) 0) ((leaf? tree) (if (odd? tree) (square tree) 0)) (else (+ (sum-odd-squares (car tree)) (sum-odd-squares (cdr tree))))))
; 构造所有偶数的费波纳奇表Fib(k), k<=给定正整数n (define (even-fibs n) (define (next k) (if (> k n) null (let ((f (fib k))) (if (even? f) (cons f (next (+ k 1))) (next (+ k 1)))))) (next 0))
事实上虽然例程看起来差别很大,但有一个内在的规律在其中:枚举器->过滤器->转换器->累积器。我们可以用表操作来实现该流操作的各部分功能。
; 过滤器 (define (filter predicate sequence) (cond ((null? sequence) null) ((predicate (car sequence)) (cons (car sequence) (filter predicate (cdr sequence)))) (else (filter predicate (cdr sequence)))))
; 累积器 (define (accumulate op initial sequence) (if (null? sequence) initial (op (car sequence) (accumulate op initial (cdr sequence)))))