SICP学习笔记(2.3.1~2.3.2)
SICP学习笔记(2.3.1~2.3.2)
周银辉
1,练习2.53
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
(define (memq2 item x)
(cond ((null? x) #f)
((eq? item (car x)) x)
(else (memq item (cdr x)))))
(list 'a 'b 'c)
(list (list 'george))
(cdr '((x1 x2) (y1 y2)))
(cadr '((x1 x2) (y1 y2)))
(pair? (car '(a short list)))
(memq 'red '((red shoes) (blue socks)))
(memq 'red '(red shoes blue socks))
运行结果是:
(a b c)
((george))
((y1 y2))
(y1 y2)
#f
#f
(red shoes blue socks)
2,eq? eqv? 与 equal?
首先,equal?为真当且仅当eqv?为真;eqv?为真当且仅当eq?为真。
其次,当两个变量引用同一个对象时eq?为真,否则为假
再次,eqv?和eq?仅在Number类型和Character类型时可能会返回不同的值
最后,当eqv?返回 #f 时,如果类型实现了equal+hash属性,则调用该属性中的方法进行对象比较。
总的说来,可以这样理解:eq?是判断两个参数是否指向同一个对象;equal?则是判断两个对象是否具有相同的结构并且结构中的内容是否相同
3,练习2.54
自定义列表比较器,为了避免和系统的equal冲突,这里取名为equal2,代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
(define (equal2? a b)
(cond ((and (null? a) (null? b)) #t)
((null? a) #f)
((null? b) #f)
(else (and (eq? (car a) (car b))
(equal2? (cdr a) (cdr b))))))
;test
(equal2? '(this is a list) '(this is a list))
(equal2? '(this is a list) '(this (is a) list))
稍稍解释一下,对于两个列表a和b:
如果a,b都为空,那么他们是相等的,否则a,b至少有一个不为空,
此时,如果a为空(那么b一定不为空),那么a,b是不相等的,
否则,如果b为空(a此时一定不为空),那么a,b是不相等的,
否则,(此时a,b都不为空),那么a,b是否相等就取决于(car a)与(car b)是否相等以及(cdr a)与(car b)是否相等
4,练习2.55
对于 (car '' a) 之所以会输出quote,是因为其实质会被解释器解释为:(quote (quote a))
下面这段代码说明了这个问题:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
(define b ''something)
;test
(equal? a b)
(car a);将(quote (quote something))中的第二个quote取出
(car b)
5,练习2.56
对于幂函数的求导,我们可以仿照加法或者乘法,先定义出幂函数表达式的构造函数,再定义其他的选择函数:判断一个表达式是否为幂函数,求基数,求幂指数,求导数....
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
;构造幂式
(define (make-exponentiation x y)
(cond ((or (=number? x 1) (=number? y 0)) 1)
((=number? y 1) x)
((=number? x 0) 0)
((and (number? x) (number? y))
(* x (make-exponentiation x (- y 1))))
(else (list '^ x y))))
;判断是否为幂式
(define (exponentiation? exp)
(and (pair? exp) (eq? (car exp) '^)))
;求幂底数
(define (base exp) (cadr exp))
;求幂指数
(define (exponent exp) (caddr exp))
在make-exponentiation 中,x表示基数,y表示幂指数,如果x为1或y为0,那么整个表达式的值为1;如果y为1,那么表达式值等同于x的值;如果x为0,则表达式的值为0;如果x和y是其他的普通数值的话,则表达式的值为y个x的连乘(这里用递归表示),否则我们将其表示为(^ x y)的形式
根据指数的求导规则 (x^n)' = n*(x^(n-1)) 所以求导函数书写如下:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
(define (derivation exp var)
(cond ((number? exp) 0)
((variable? exp)
(if (same-variable exp var) 1 0))
((sum? exp)
(make-sum (derivation (addend exp) var)
(derivation (augend exp) var)))
((product? exp)
(make-sum
(make-product (multiplier exp)
(derivation (multiplicand exp) var))
(make-product (multiplicand exp)
(derivation (multiplier exp) var))))
((exponentiation? exp)
(let ((expt (exponent exp))
(bs (base exp)))
(make-product
(make-product expt
(make-exponentiation bs (- expt 1)))
(derivation bs var))))
(else
(error "error~"))))
完整的代码在这里:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
输出结果为:
(+ (* x y) (* (+ x 3) y))
(* y (* 5 (^ x 4)))
6,练习2.57
如果按照上面的导数函数定义来计算下面的这个表达式的话:
(derivation '(* x y (+ x 3)) 'x)
将得到的结果是 y, 很不幸,这明显错误了,看来我们得改改,首先,幂式不用改,其始终只接受两个参数,所以需要改的是加法和乘法,让它们可以支持多个参数,但有一点:我想重用我们已经写好的make-sum,make-product函数。要完成这个任务,我们需要先做一个简单的练习:我们来写一个普通的加法运算函数,让它支持多个参数(我们假设scheme内置的“+”号仅能接受两个参数)
我们先定义一个函数,它仅能接受两个普通数值作为参数:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
(define (make-sum-two x y)
(+ x y))
然后再定义一个高级点的,其虽然也只能够接受两个参数,但第二个参数是一个列表,它重用了上面的函数:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
(define (make-sum-list x list)
(if (null? list)
x
(make-sum-two x (make-sum-list (car list) (cdr list)))))
有了这个函数,假设我们想计算1到5这五个数字的连加的话,就可以这样书写(make-sum-list 1 '(2 3 4 5))
呵呵,虽然能很好的工作,但这样的书写难免有些“不够正派”。没关系,记得在SICP2.2节,练习题2.20提到了一个称之为“带点尾部记法”的形式(与C语言的可变参数类似),我们可以加以利用,于是便有了第三个函数:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
(define (make-sum-any x . y)
(make-sum-list x y))
这下我们可以如下书写了: (make-sum-any 1 2 3 4 5)
我们发现,通过make-sum-list作桥梁,我们可以将仅能接受两个参数的make-sum-two转化成可以接受任意个参数的make-sum-any,并且不用关心make-sum-two原来的实现细节,更不用修改它,同样的道理,我们可以按照这种方式来更新练习2.56中的make-sum 和 make-product函数,让他们支持多个参数:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
;构造和式
(define (make-sum x y)
(cond ((=number? x 0) y)
((=number? y 0) x)
((and (number? x) (number? y)) (+ x y))
(else (list '+ x y))))
;它可以接受一个数值参数和一个列表参数
(define (make-sum-list x list)
(if (null? list)
x
(make-sum x (make-sum-list (car list) (cdr list)))))
;它可以接受任意多个数值参数
(define (make-sum-any x . y)
(make-sum-list x y))
;构造乘式
(define (make-product x y)
(cond ((or (=number? x 0) (=number? y 0)) 0)
((=number? x 1) y)
((=number? y 1) x)
((and (number? x) (number? y)) (* x y))
(else (list '* x y))))
;它可以接受一个数值参数和一个列表参数
(define (make-product-list x list)
(if (null? list)
x
(make-product x (make-product-list (car list) (cdr list)))))
;它可以接受任意多个数值参数
(define (make-product-any x . y)
(make-product-list x y))
到目前为止,工作仍没做完,因为我们发现原来的求被加数和被乘数的选择函数不适用了,那加法来说, (+ 1 2) 与(+ 1 2 3)的加数都是1,前者的被加数是2,而后者的被加数呢?我觉得是(+ 2 3) ,这里我没有什么理论依据,但其工作得很好,此外,你也可以将(+ 1 2 3) 看着是 (+ 1 (+ 2 3)),所以,加数是1,被加数是 (+ 2 3) , 乘法同理,那么我们求被加数和被乘数的选择函数就可以如下定义:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
(define (augend exp)
(let ((L (length exp)))
(cond ((= L 3) (caddr exp))
((= L 2) (cadr exp))
((= L 1) 0)
(else (cons '+ (cddr exp))))))
(define (multiplicand exp)
(let ((L (length exp)))
(cond ((= L 3) (caddr exp))
((= L 2) (cadr exp))
((= L 1) 1)
(else (cons '* (cddr exp))))))
OK,到此为止,我们可以得到该习题的完整解答:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
;测试是否为简单变量
(define (variable? x) (symbol? x))
;测试一个变量是否等于某个数
(define (=number? x a)
(and (number? x) (= x a)))
;测试两个变量是否相等
(define (same-variable x y)
(and (variable? x) (variable? y) (eq? x y)))
;构造幂式
(define (make-exponentiation x y)
(cond ((or (=number? x 1) (=number? y 0)) 1)
((=number? y 1) x)
((=number? x 0) 0)
((and (number? x) (number? y))
(* x (make-exponentiation x (- y 1))))
(else (list '^ x y))))
;判断是否为幂式
(define (exponentiation? exp)
(and (pair? exp) (eq? (car exp) '^)))
;求幂底数
(define (base exp) (cadr exp))
;求幂指数
(define (exponent exp) (caddr exp))
;构造和式
(define (make-sum x y)
(cond ((=number? x 0) y)
((=number? y 0) x)
((and (number? x) (number? y)) (+ x y))
(else (list '+ x y))))
;它可以接受一个数值参数和一个列表参数
(define (make-sum-list x list)
(if (null? list)
x
(make-sum x (make-sum-list (car list) (cdr list)))))
;它可以接受任意多个数值参数
(define (make-sum-any x . y)
(make-sum-list x y))
;判断表达式是否为和式
(define (sum? x)
(and (pair? x) (eq? (car x) '+)))
;求和式中的加数
(define (addend exp) (cadr exp))
;求和式中的被加数
(define (augend exp)
(let ((L (length exp)))
(cond ((= L 3) (caddr exp))
((= L 2) (cadr exp))
((= L 1) 0)
(else (cons '+ (cddr exp))))))
;构造乘式
(define (make-product x y)
(cond ((or (=number? x 0) (=number? y 0)) 0)
((=number? x 1) y)
((=number? y 1) x)
((and (number? x) (number? y)) (* x y))
(else (list '* x y))))
;它可以接受一个数值参数和一个列表参数
(define (make-product-list x list)
(if (null? list)
x
(make-product x (make-product-list (car list) (cdr list)))))
;它可以接受任意多个数值参数
(define (make-product-any x . y)
(make-product-list x y))
;判断表示式是否为乘式
(define (product? x)
(and (pair? x) (eq? (car x) '*)))
;求乘式中的乘数
(define (multiplier exp) (cadr exp))
;求乘式中的被乘数
(define (multiplicand exp)
(let ((L (length exp)))
(cond ((= L 3) (caddr exp))
((= L 2) (cadr exp))
((= L 1) 1)
(else (cons '* (cddr exp))))))
;求导数(没能包含对除法的求导)
(define (derivation exp var)
(cond ((null? exp) 0)
((number? exp) 0)
((variable? exp)
(if (same-variable exp var) 1 0))
((sum? exp)
(make-sum-any (derivation (addend exp) var)
(derivation (augend exp) var)))
((product? exp)
(make-sum-any
(make-product-any (multiplier exp)
(derivation (multiplicand exp) var))
(make-product-any (multiplicand exp)
(derivation (multiplier exp) var))))
((exponentiation? exp)
(let ((expt (exponent exp))
(bs (base exp)))
(make-product-any
(make-product-any expt
(make-exponentiation bs (- expt 1)))
(derivation bs var))))
(else
(begin (display "\nerror~")
(display exp)
(display " and ")
(display var)
(display "\n")))))
;test
(derivation '(+ (^ x 2) (* 2 x y) y) 'x)
(derivation '(+ (* x x) (* 2 x y) y) 'x)
(derivation '(* x y (+ x 3)) 'x)
7,练习2.58
修改为中缀表达式,直接贴代码了:
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
(define (variable? x) (symbol? x))
(define (=number? x a)
(and (number? x) (= x a)))
(define (same-variable? x y)
(and (variable? x) (variable? y) (eq? x y)))
(define (make-sum a1 a2)
(cond ((=number? a1 0) a2)
((=number? a2 0) a1)
((and (number? a1) (number? a2)) (+ a1 a2))
(else (list a1 '+ a2))))
(define (sum? x)
(and (pair? x) (eq? (cadr x) '+)))
(define (addend exp) (car exp))
(define (augend exp) (caddr exp))
(define (make-product m1 m2)
(cond ((or (=number? m1 0) (=number? m2 0)) 0)
((=number? m1 1) m2)
((=number? m2 1) m1)
((and (number? m1) (number? m2)) (* m1 m2))
(else (list m1 '* m2))))
(define (product? x)
(and (pair? x) (eq? (cadr x) '*)))
(define (multiplier exp) (car exp))
(define (multiplicand exp) (caddr exp))
(define (make-exponentiation base exponent)
(cond ((=number? exponent 0) 1)
((=number? exponent 1) base)
(else (list base '** exponent))))
(define (exponentiation? x)
(and (pair? x) (eq? (cadr x) '**)))
(define (base exponentiation) (car exponentiation))
(define (exponent exponentiation) (caddr exponentiation))
(define (derivation exp var)
(cond ((number? exp) 0)
((variable? exp)
(if (same-variable? exp var) 1 0))
((sum? exp)
(make-sum (derivation (addend exp) var)
(derivation (augend exp) var)))
((product? exp)
(make-sum
(make-product (multiplier exp)
(derivation (multiplicand exp) var))
(make-product (multiplicand exp)
(derivation (multiplier exp) var))))
((exponentiation? exp)
(let ((expt (exponent exp))
(bs (base exp)))
(make-product
(make-product expt
(make-exponentiation bs (- expt 1)))
(derivation bs var))))
(else
(error "error~"))))
;test
(derivation '((x * y) * (x + 3)) 'x)
注:这是一篇读书笔记,所以其中的内容仅 属个人理解而不代表SICP的观点,并随着理解的深入其中 的内容可能会被修改