SICP学习笔记(2.1.1 ~ 2.1.3)

                                                 SICP学习笔记(2.1.1 ~ 2.1.3)
                                                        周银辉

 

这几节相对比较简单,主要引入“抽象”和“封装”等一些思想,作者花了不少笔墨想将“抽象”的思想注入到读者的头脑的,但如果先前学过一些面向对象编程的话,这几节就可快速通过了(除了2.1.3,此节的内容有些醍醐灌顶,稍后再说) 

1,练习2.1

按照题目的意思,“如果分母分子同号,则分子分母都化为正;否则,分子化为负,分母化为正”。我将这句话转换成了下面这句话“如果分母为负,则分子分母都取相反数;否则分子分母保持不变”,两句话是对等的,不信试试看:

(define (make-rat n d)
  ;如果分母为负,则分子分母都取相反数
  (cond ((< d 0) (cons (- 0 n) (- 0 d)))
        ;否则分子分母保持不变
        (else (cons n d))))

(define (num z)
  (car z))

(define (den z)
  (cdr z))

(define a (make-rat 2 3))
(define b (make-rat -2 3))
(define c (make-rat 2 -3))
(define d (make-rat -2 -3))

;测试函数
(= (num a) 2)
(= (den a) 3)
(= (num b) -2)
(= (den b) 3)
(= (num c) -2)
(= (den c) 3)
(= (num d) 2)
(= (den d) 3)

上面的输出全部是#t (True),说明正确哈。 

2,练习2.2

首先由序对构造点,然后由点构造线段,最后定义其他函数:

(define (make-point x y)
  (cons x y))

(define (x-point p)
  (car p))

(define (y-point p)
  (cdr p))

(define (print-point p)
   (newline)
   (display "(")
   (display (x-point p))
   (display ",")
   (display (y-point p))
   (display ")"))

(define (make-segment a b)
  (cons a b))

(define (start-segment s)
  (car s))

(define (end-segment s)
  (cdr s))

(define (mid-segment s)
   (let ((start (start-segment s))
         (end   (end-segment s)))   
     (make-point (/ (+ (x-point start) (x-point end)) 2)
                 (/ (+ (y-point start) (y-point end)) 2))))

;test
(print-point (mid-segment (make-segment (make-point 2 3) (make-point 4 5))))

输出为 (3,4)

3, 练习2.3
求矩形周长和面积,直接略过了哈,比较简单。

4,数据意味着什么?

关于2.1.3,真的很想总结出点什么,但真的不知道如何总结,正如作者在脚注中所言“令人吃惊的是,将这一思想严格地形式化却非常困难”,只默默地建议仔细地通篇阅读,的确让“每天都在吼面向对象的我们”有些醍醐灌顶,正如用如下方式来定义序对(Pair)一样:
class Program
   {
       static void Main(string[] args)
       {
           var pair = MakePair(2, 3);

           Console.WriteLine(GetFirstElement(pair));
           Console.WriteLine(GetSecondElement(pair));
           DisplayPair(pair);

           Console.Read();
       }

       public delegate object PairDelegate(bool flag);

       public static PairDelegate MakePair(object a, object b)
       {
           return (flag) => { return (flag ? a : b); };
       }

       public static object GetFirstElement(PairDelegate pair)
       {
           return pair(true);
       }

       public static object GetSecondElement(PairDelegate pair)
       {
           return pair(false);
       }

       public static void DisplayPair(PairDelegate pair)
       {
           Console.WriteLine("( "+ GetFirstElement(pair) +" , "+ GetSecondElement(pair)+" )");
       }
   }

5,练习2.4

(define (cons2 x y)
  (lambda (m) (m x y)))

(define (car2 z)
  (z (lambda (p q) p)))

(define (cdr2 z)
  (z (lambda (p q) q)))

;test
(define a (cons2 3 5))
(car2 a)
(cdr2 a)

为了避免和系统预定义的标识符发生冲突,我将cons car cdr 后面都加了一个2。
要解此题关键在(lambda (m) (m x y)) 应该注意到这里的m应该是一个过程,而非普通变量。所以对(define (cons2 x y) (lambda (m) (m x y))) 的理解就应该变成“所谓cons2,就是它接受一个过程,并将该过程应用到参数x和y”。那么要定义car2,则应该“传入一个过程,该过程会接受两个参数,并且返回第一个参数”,同理,要定义cdr2,则应该“传入一个过程,该过程会接受两个参数,并且返回第二个参数”。

6,练习2.5

如果 (2^a) * (3^b) = z, 求 a 和 b

这个题比较有意思,表面上看一个二元方程只有一个表达式,貌似求不出来。这这个题的中幂底数比较巧合:一个奇数和一个偶数。所以,对于求a,如果m为奇数的话,那么a一定为0,否则 a = a‘ + 1, 其中a‘是 (2^a‘) * (3^b) = z/2的解,所以代码如下:

;定义 a^n
(define (pow a n)
  (define (pow-inner a counter result)
    (if (= 0 counter)
        result
        (pow-inner a (- counter 1) (* result a))))
  (pow-inner a n 1))

;定义序对
(define (cons2 a b)
  (* (pow 2 a) (pow 3 b)))

;定义car
(define (car2 z)
  (if (= (remainder z 2) 0)
      (+ 1 (car2 (/ z 2)))
      0))

同理,如果 z 除以3的余数不为0,那么b一定为0,否则 b = b‘ + 1, 其中 b‘ 是(2^a) * (3^b‘) = z/3的解。

;定义cdr
(define (cdr2 z)
(if (= (remainder z 3) 0)
     (+ 1 (cdr2 (/ z 3)))
     0))

7,练习2.6

SICP学习笔记(1.3.2 ~ 1.3.3)的“lambda计数”中已经给出答案了,跳转到那里去找答案吧。

 

注:这是一篇读书笔记,所以其中的内容仅属个人理解而不代表SICP的观点,并随着理解的深入其中的内容可能会被修改

 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值