the little schemer 笔记(3)

第三章 cons the magnificent

(rember a lat)是什么,其中a是mint,lat是(lamb chops and mint jelly)

(lamb chops and jelly)。"rember"表示remove a member 删除一个成员。



(rember a lat)是什么,其中a是mint,lat是(lamb chops and mint flavored mint jelly)

(lamb chops and flavored jelly)




(rember a lat)是什么,其中a是toast,lat是(bacom lettuce and tomato)

(lamb chops and flavored jelly)





(rember a lat)是什么,其中a是cup,lat是(coffee cup tea and hick cup)

(coffee tea and hick cup)





(rember a lat)做什么

删除lat中的第一个和第一个参数atom原子相同的atom原子



那么我们应该做哪些步骤

首先测试(null? lat)——这是第一戒



如果(null? lat)是真呢

返回()。



如果(null? lat)是假呢

至少lat中有一个atom原子



需要其它查询吗

不需要。要么lat是空要么至少有一个原子。



如果至少有一个atom原子呢

查询a是否与(car lat)相同。



怎么查询

使用
(cond
  (_____ ______)
  (_____ ______)



怎么查询a是否与(car lat)相同

(eq? (car lat) a)



如果a与(car lat)相同那么(rember a lat)的值是什么

(cdr lat)



如果a与(car lat)不相同那么(rember a lat)的值是什么

保留(car lat),但是寻找lat的其他部分是否有a



怎么删去lat其余部分的第一个a

(rember a (cdr lat)



还有查询吗

没了



现在让我们写出刚才想到的函数rember
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define rember
  (lambda (a lat)
    (cond
      ((null? lat) '())
      (else (cond
              ((eq? a (car lat)) (cdr lat))
              (else (rember a (cdr lat))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(rember a lat)的值是什么,其中a是bacon,lat是(bacon lettuce and tomato)

(lettuce and tomato)。提示写下rember函数和参数,然后参考下面的系列问题。




看看函数是否正常。第一个查询是什么

(null? lat)



现在呢

下一个查询。




else

是。


然后呢

下一个查询。




(eq? (car lat) a)

是,所以值是(cdr lat),在此例子中为(lettuce and tomato)。




这是正确的吗

是的,所以值是(cdr lat)。此例中为(lettuce and tomato)
在DrRacket中运行
;;scheme code
;;the little schemer
;;chapter 3
(define rember
  (lambda (a lat)
    (cond
      ((null? lat) '())
      (else (cond
              ((eq? (car lat) a) (cdr lat))
              (else (rember a (cdr lat))))))))

(rember 'bacon '(bacon lettuce and tomato))
;;scheme code

输出为
(lettuce and tomato)



但是,我们的用例对么

谁知道啊?但是,证明甜点是用吃,让我们试试其它例子。



rember是做什么的

它的参数为一个atom原子和一个lat,rember在第二个参数中寻找出第一个出现的第一个参数并把它删除。



现在呢

把a对比lat中的每一个atom原子,如果没有找到,那么我们把刚才对比的原子放入新的list中。



(rember a lat)是什么值,其中a是and,lat是(bacon lettuce and tomato)

(bacon lettuce tomato)




让我们看看我们的rember函数是否正确。rember的第一个查询是什么

(null? lat)



下面呢

下一个查询。



else

OK,下一个问题



(eq? (car lat)a)

否,下个查询



(else (rember a (cdr lat)))是什么意思

else询问真假。下一行用参数a和(cdr lat)递归查询,a是and,(cdr lat)是(lettuce  and tomato)




(null? lat)

否,下个查询



else

确定



(eq? (car lat) a)

否,下个查询




(rember a (cdr lat)) 的意思是什么

递归查询,a是and,(cdr lat)是(and tomato)



(null? lat)

否,下个查询


else

当然of course




(eq? (car lat) a)

是。



所以结果是什么

(cdr lat)即(tomato)
;;scheme code
;;the little schemer
;;chapter 3
(define rember
  (lambda (a lat)
    (cond
      ((null? lat) '())
      (else (cond
              ((eq? (car lat) a) (cdr lat))
              (else (rember a (cdr lat))))))))

(rember 'and '(bacon lettuce and tomato))
;;scheme code

输出为
(tomato)


正确吗

不对,正确的应该是(bacom lettuce and tomato)



我们做错什么了

我们把and,以及and以前的东西都丢弃了。



我们怎么才能留住bacon和lettuce

使用cons,还记得第一章的cons吗



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
第二戒
使用cons构建list表
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



让我们看看使用了cons后的rember函数
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define rember
  (lambda (a lat)
    (cond
      ((null? lat) '())
      (else (cond
              ((eq? (car lat) a) (cdr lat))
              (else (cons (car lat) (rember a (cdr lat)))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
运行一下代码
;;the little schemer
;;chapter 3
(define rember
  (lambda (a lat)
    (cond
      ((null? lat) '())
      (else (cond
              ((eq? (car lat) a) (cdr lat))
              (else (cons (car lat) (rember a (cdr lat)))))))))

(rember 'and '(bacon lettuce and tomato))
;;

输出为
(tomato)



另外一个版本的rember函数为
;;the little schemer
;;chapter 3
;;another function rember

(define rember
  (lambda (a lat)
    (cond
      ((null? lat) '())
      ((eq? (car lat) a) (cdr lat))
      (else (cons (car lat) (rember a (cdr lat)))))))
;;测试下
(rember 'and '(bacon lettuce and tomato))

DrRacket环境测试结果为
(bacon lettuce tomato)



这个版本是否简单点呢

像rember这样的函数可以简化成这样



为什么要化简

那样函数的结构与参数的结构就不一致了



笔记:其实cond就是c/c++等中的swith嘛,else就是default嘛。




(firsts l)是什么,其中l是
((apple peach pumpkin)
 (plum pear cherry)
 (grape rasin pea)
 (bean carrot eeggplant))

(apple plum grape bean)





(firsts l)是什么,其中l是((a b) (c d) (e f))

(a c e)



(firsts l)是什么,其中l是()

()



(firsts l)是什么,其中l是
(((five plums) four)
 (eleven green oranges)
 ((no) more))

((five plums) eleven (no))



现在,用你自己的话说说(firsts l)做什么

下面是我们的尝试
“firsts函数输入一个lists表参数,null空表,或者只包含非空表。它抽取list表中每一个成员的第一个 S-expression表达式构建新的list表。”



试试你能不能写出函数firsts,记得戒律。

(define firsts
  (lambda (l)
    (cond
      ((null? l) ...)
      (else (cons ... (firsts (cdr l)))))))



为什么要
(define firsts
  (lambda (l)
    ...))

因为要定义函数名firsts和参数(l)



为什么(cond ...)

因为我们对参数需要查询问题



为什么要
((null? l) ...)

第一戒



为什么(else

因为我们对参数l只有两个查询。笔记:else相当与c/c++的default




为什么(else

看上面。另外最后一个查询必须总是else




为什么(cons

因为我们需要创建list表——第二戒



为什么)))

与(cond, (lambda, (define, 的括号匹配。



回忆下firsts的定义。(firsts l)的典型元素是什么

a



下一个典型元素是什么
((a b) (c d) (e f))

c,还有后边的e



考虑一下seconds的定义。(seconds l)的元素是什么,其中l是((a b) (c d) (e f))

b,d, 或者f。




我们怎么描述(firsts l)的典型元素

参照第一章,那么l的一个元素的car为(car (car l))



找到一个典型的(firsts l)后,我们该做什么

把它cons到递归(firsts (cdr l))中。




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
第三戒
当构建list表时,描述第一个典型元素,然后把它cons到自然递归中去。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



有了第三戒,我们可以添入firsts函数更多部分
(else (cons (car (car l)) (firsts (cdr l)) ))
            ~~~~~~~~~~~~  ^^^^^^^^^^^^^^^^
              典型元素      自然递归




(firsts l)结果是什么,其中l是((a b) (c d) (e f))
(define firsts
  (lambda (l)
    (cond
      ((null? l) ...)
      (else (cons (car (car l)) (firsts (cdr l)))))))

什么也没有。我们的菜谱中还缺点重要的成分。不过,我们现在可以就可以执行它。



(null? l) 其中l是l((a b) (c d) (e f))

否,下个查询




下面的代码是什么值
(cons (car (car l)) (firsts (cdr l)))

把(car (car l))cons到(firsts (cdr l))。为了找出(firsts (cdr )),我们用新参数(cdr l)递归函数



(null? l)其中l是((c d) (e f))

否,下个查询




下面的代码是什么值
(cons (car (car l)) (firsts (cdr l)))

保存(car (car l)),递归(firsts (cdr l))



(null? l)其中l是((e f))

否,下个查询




下面的代码是什么值
(cons (car (car l)) (firsts (cdr l)))

把(car (car l))cons到(firsts (cdr l))。为了找出(firsts (cdr )),我们用新参数(cdr l)递归函数



(null? l)其中l是((c d) (e f))

否,下个查询




下面的代码是什么值
(cons (car (car l)) (firsts (cdr l)))

保存(car (car l)),递归(firsts (cdr l))



(null? l)其中l是()

是。



现在((null? l) ...)的值是什么

没有值,缺了点的东西。



我们应该把原子cons到什么上?

一个list。还记得cons的规则。




为了达到cons,当(null? l)为真时,应该是什么值。

既然最后的值必须是一个list,#t或#f都不对。让我们试试(quote ())




我们需要依次cons三次到一个()中。我们可以:

I.这样:
1. 把e cons 到()
2. 把c cons 到1的输出
3. 把a cosn 到2的输出

II.或者这样:
1. 把a cons 到2的输出
2. 把c cons 到3的输出
3. 把e cons 到()

III.或者这样:
把 a cons 到
  把 c cons 到
    把e cons 到
      ()

不论如何(firsts l)结果都是 (a c e)





以上三种哪种是最舒服的呢

对啊,就选那个




;;the little schemer
;;chapter 3
;;function firsts
(define firsts
  (lambda (l)
    (cond
      ((null? l) '())
      (else (cons (car (car l)) (firsts (cdr l)))))))

'((a b) (c d))
(firsts '((a b) (c d)))

DrRacket环境测试结果为

((a b) (c d))
(a c





(insertR new old lat)是什么,其中new是topping,old是fudge,lat是(ice cream with fudge for dessert)

(ice ceame with fudge topping for dessert)




(insertR new old lat)是什么,其中new是jalapeno,old是and,lat是(tacos tamales and salsa)

(tacos tamales and jalapeno salsa)




(insertR new old lat)是什么,其中new是e,old是d,lat是 (a b c d e f g d h)

(a b c d e f g d h)




用你的话描述一下(insertR new old lat)是做什么的

这里是我们的叙述:
“它有三个参数:atom原子new和old,还有一个lat。函数insertR把new插入到第一个old后边创建一个lat。”



试试看你能不能写出插入函数insertR
;;scheme
(define insertR
  (lambda (new old lat)
    (cond ...)))




当递归insertR时,哪个参数变化了

lat,因为我们次只能查询一个atom原子。



我们要查询多少次lat

两次。lat要么是null空表,要么是非空表。



我么哦你需要问什么

首先,我们问(null? lat)。第二else,因为else是做后一个查询。



如果(null? lat)假呢

那么至少lat有一个a成员。



我们对第一个成员第一个查询问什么

首先,(eq? (car lat) old)。然后是else,没有其他情况了。




现在试试看你能不能写出整个insertR函数
下面是我们的尝试
;;the little schemer
;;chapter 3
(define insertR
  (lambda (new old lat)
    (cond
      ((null? lat) (quote()))
      (else (cond
              ((eq? old (car lat)) (cdr lat))
              (else (cons (car lat) (insertR new old (cdr lat)))))))))

'(ice cream with fudge for dessert)
(insertR 'topping 'fudge '(ice cream with fudge for dessert))

DrRacket环境测试结果为
(ice cream with fudge for dessert)
(ice cream with for dessert)




可以看到new的地方被删除了,仅此而已。这一rember一样了。当(eq? (car lat) old)是真时该做什么

在old的右侧插入new



怎么做

把new cons到(cdr lat)上



现在的insertR的函数定义为
;;the little schemer
;;chapter 3
(define insertR
  (lambda (new old lat)
    (cond
      ((null? lat) (quote()))
      (else (cond
              ((eq? old (car lat)) (cons new (cdr lat)))
              (else (cons (car lat) (insertR new old (cdr lat)))))))))

'(ice cream with fudge for dessert)
(insertR 'topping 'fudge '(ice cream with fudge for dessert))

DrRacket环境测试结果为
(ice cream with fudge for dessert)
(ice cream with topping for dessert)




这是我们想要的吗

不是,这是把new替换了old




还要做什么

需要把old cons到刚刚cons到new前边。



怎么把old加到new前边

把new cons到(cdr lat)上,再把old cons上即
(cons old (cons new (cdr lat)



现在写出insertR函数
;;the little schemer
;;chapter 3
(define insertR
  (lambda (new old lat)
    (cond
      ((null? lat) (quote()))
      (else (cond
              ((eq? old (car lat)) (cons old (cons new (cdr lat))))
              (else (cons (car lat) (insertR new old (cdr lat)))))))))

'(ice cream with fudge for dessert)
(insertR 'topping 'fudge '(ice cream with fudge for dessert))

DrRacket环境测试结果为
(ice cream with fudge for dessert)
(ice cream with fudge topping for dessert)

完全正常



现在试一试insertL函数
提示:insertL把new原子插入到lat中的第一个old的左边。

这个容易多了,对吗
;;the little schemer
;;chapter 3
(define insertL
  (lambda (new old lat)
    (cond
      ((null? lat) (quote ()))
      (else (cond
              ((eq? old (car lat)) (cons new lat))
              (else (cons (car lat) (insertL new old (cdr lat)))))))))

'(ice cream with fudge for dessert)
(insertL 'topping 'fudge '(ice cream with fudge for dessert))

DrRacket环境测试结果为
(ice cream with fudge for dessert)
(ice cream with topping fudge for dessert)



现在试试看你能不能写出subst函数
提示:(subst new old lat)用new替换lat中的第一个old

当然就是
;;the little schemer
;;chapter 3
(define subst
  (lambda (new old lat)
    (cond
      ((null? lat) (quote()))
      (else (cond
              ((eq? old (car lat)) (cons new (cdr lat)))
              (else (cons (car lat) (subst new old (cdr lat)))))))))

'(ice cream with fudge for dessert)
(subst 'topping 'fudge '(ice cream with fudge for dessert))

DrRacket环境测试结果为
(ice cream with fudge for dessert)
(ice cream with topping for dessert)



来,cons一块蛋糕到嘴里吧。



现在试试看你能不能写出subst2函数
提示:(subst2 new o1 o2 lat)
把lat中第一个出现的o1或者第一个出现的o2替换为new
例如,new是vanilla,o1是chocolate,o2是banana,lat是(banana ice cream with chocolate topping)

值是(banana ice cream with chocolate topping)

可以这样
;;the little schemer
;;chapter 3
(define subst2
  (lambda (new o1 o2 lat)
    (cond
      ((null? lat) (quote()))
      (else (cond
              ((eq? o1 (car lat)) (cons new (cdr lat)))
              ((eq? o2 (car lat)) (cons new (cdr lat)))
              (else (cons (car lat) (subst new old (cdr lat)))))))))

'(banana ice cream with chocolate topping)
(subst2 'vanilla 'chocolate 'banana '(banana ice cream with chocolate topping))
                                  
                   
也可以这样,把o1和o2直接用or表示
;;the little schemer
;;chapter 3
(define subst2
  (lambda (new o1 o2 lat)
    (cond
      ((null? lat) (quote()))
      (else (cond
              ((or (eq? o1 (car lat)) (eq? o2 (car lat))) (cons new (cdr lat)))
              (else (cons (car lat) (subst new old (cdr lat)))))))))

'(banana ice cream with chocolate topping)
(subst2 'vanilla 'chocolate 'banana '(banana ice cream with chocolate topping))
                                  
当然DrRacket环境测试结果是一样的

(banana ice cream with chocolate topping)
(vanilla ice cream with chocolate topping)


如果你直接写出最后一种,再cons一块蛋糕到嘴里吧。



还记得rember是做什么的吗

rember查找lat中的每一个atom看是否与a相同。如果不相同,rember留下这个atom。当第一次遇到a时,它停止并给出(cdr lat),即lat的剩余部分。所以最终仅仅删除了lat中的a。



写出函数 multirember ,它把lat中所有的a都删除

(define multirember
  (lambda (a lat)
    (cond
      (_____ _____)
      (else
        (cond
          (_____ _____)
          (_____ _____))))))

提示:当(eq? (car lat) a)是真时该做什么
考虑一下例子,a是cup,lat是(cup tea cup and hick cup)



(define multirember
  (lambda (a lat)
    (cond
      ((null? lat) (quote ()))
      ((eq? a (car lat)) (multirember a (cdr lat)))
      (else (cons (car lat) (multirember a (cdr lat)))))))

'(cup tea cup and hick cup)
(multirember 'cup '(coffee cup tea cup and hick cup))


DrRacket环境测试结果为
(coffee cup tea cup and hick cup)
(coffee tea and hick)



你知道multirember如何工作的吗

不知道没关系,下边一步步的看。



(null? lat)

否,下个查询



(eq? (car lat) a)

否,下个查询



下面的代码是什么意思
(cons (car lat) (multirember a (cdr lat)))



保存(car lat)即coffee,cons到一会计算的(multirember a (cdr lat)))上。现在看(multirember a (cdr lat)))



(null? lat)

否,下个查询




else

当然




(eq? (car lat) a)

是,所以忘记(car lat),直接看(multirember a (cdr lat))



(null? lat)

否,下个查询



(eq? (car lat) a)

否,下个查询




下面的代码是什么意思
(cons (car lat) (multirember a (cdr lat)))



保存(car lat)即coffee,cons到一会计算的(multirember a (cdr lat)))上。现在看(multirember a (cdr lat)))




(null? lat)

否,下个查询



else
OK,下一个问题



(eq? (car lat) a)

是,所以忘记(car lat),直接看(multirember a (cdr lat))




(null? lat)

否,下个查询



(eq? (car lat) a)

否,下个查询




下面的代码是什么意思
(cons (car lat) (multirember a (cdr lat)))



保存(car lat)即coffee,cons到一会计算的(multirember a (cdr lat)))上。现在看(multirember a (cdr lat)))




(null? lat)

否,下个查询



(eq? (car lat) a)

否,下个查询




下面的代码是什么意思
(cons (car lat) (multirember a (cdr lat)))



保存(car lat)即coffee,cons到一会计算的(multirember a (cdr lat)))上。现在看(multirember a (cdr lat)))




(null? lat)

否,下个查询



(eq? (car lat) a)

是,所以忘记(car lat),直接看(multirember a (cdr lat))




(null? lat)

是,所以值是()



结束了吗

没有,还有几个cons没执行,需要回溯递归。




下面呢

把最近一次的(car lat)即hick cons 到()上。



下面呢

把and cons到(hick)



下面呢

把tea cons 到 (and hick)




下面呢

把coffee cons到(tea and hick)




结束了吗

yes



现在写一下函数 multiinsertR
(define multiinsertR
  (lambda (new old lat)
    (cond
      (_____ _____)
      (else
        (cond
          (_____ _____)
          (_____ _____))))))


;;the little schemer
;;chapter 3
(define multiinsertR
  (lambda (new old lat)
    (cond
      ((null? lat) (quote()))
      (else (cond
              ((eq? old (car lat)) (cons old (cons new (multiinsertR new old (cdr lat)))))
              (else (cons (car lat) (multiinsertR new old (cdr lat)))))))))

'(coffee cup tea cup and hick cup)
(multiinsertR 'tastes-very-good 'cup '(coffee cup tea cup and hick cup))
 
DrRacket环境测试结果为
(coffee cup tea cup and hick cup)
(coffee cup tastes-very-good tea cup tastes-very-good and hick cup tastes-very-good)




下面这个函数正确吗
;;the little schemer
;;chapter 3
(define multiinsertL
  (lambda (new old lat)
    (cond
      ((null? lat) (quote ()))
      (else (cond
              ((eq? old (car lat)) (cons new (cons old (multiinsertL new old lat))))
              (else (cons (car lat) (multiinsertL new old (cdr lat)))))))))

'(coffee cup tea cup and hick cup)
(multiinsertL 'milk 'cup '(coffee cup tea cup and hick cup))



这个程序能结束吗

No,因为遇到第一个old后一直不停的往它左边 (cons new (cons old ... 直到内存耗尽。



现在试试更改的 multiinsertL 函数
;;the little schemer
;;chapter 3
(define multiinsertL
  (lambda (new old lat)
    (cond
      ((null? lat) (quote ()))
      (else (cond
              ((eq? old (car lat)) (cons new (cons old (multiinsertL new old (cdr lat)))))
              (else (cons (car lat) (multiinsertL new old (cdr lat)))))))))

'(coffee cup tea cup and hick cup)
(multiinsertL 'milk 'cup '(coffee cup tea cup and hick cup))

DrRacket环境测试结果为
(coffee cup tea cup and hick cup)
(coffee milk cup tea milk cup and hick milk cup)




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
第十戒 (初版)
递归时至少要有一个参数变化,并且向终止条件方向变化。变化的参数必须有终止测试条件:当时用cdr时,用null?测试终止。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;




现在我们写个 multisubst 函数
(define multisubst
  (lambda (new old lat)
    (cond
      (_____ ______)
      (else
        (cond
          (_____ _____)
          (_____ _____))))))




;;the little schemer
;;chapter 3
(define multisubst
  (lambda (new old lat)
    (cond
      ((null? lat) (quote()))
      (else (cond
              ((eq? old (car lat)) (cons new (multisubst new old (cdr lat))))
              (else (cons (car lat) (multisubst new old (cdr lat)))))))))

'(coffee cup tea cup and hick cup)
(multisubst 'cake 'cup '(coffee cup tea cup and hick cup))

DrRacket环境测试结果为
(coffee cup tea cup and hick cup)
(coffee cake tea cake and hick cake)

 

 

 

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 China Mainland License.

posted @ 2012-08-17 22:39  Z.X.L  阅读(632)  评论(0编辑  收藏  举报