the little schemer 笔记(8)

第八章 lambda the ultimate



还记得我们第五章末的rember和insertL吗

我们用equal?替换了eq?



你能用你eq?或者equal?写一个函数rember-f吗

还不能,因为我们还没告诉你怎么弄



你如何用rember删除 (b c a) 中的第一个a元素吗

把参数a和(b c a)传给rember




你如何用rember删除 (b c a) 中的第一个c元素吗

把参数c和(b c a)传给rember



你如何能够把rember-f的eq?用eq?代替

把equal? 作为参数传给 rember

笔记:这和C语言的函数指针作用一样啊。



(rember-f test? a l)是什么,其中test?是=,a是5,l是(6 3 5 3)

(6 2 3)
注:
Lisp:(rember-f (function =) 5 '(6 2 5 3))



(rember-f test? a l) 是什么,其中test? 是eq?,a是jelly,l是(jelly beans are good)

(beans are good)




(rember-f test? a l) 是什么,其中test? 是equal?,a是(pop corn),l是(lemonade (pop corn) and (cake))

(lemonade and (cake))



试着写出函数 rember-f

(define rember-f
  (lambda (test? a l)
    ((null? l) (quote ()))
    (else (cond
            ((test? (car l) a) (cdr l))
            (else (cons (car l) (rember-f test? a (cdr l))))))))
注:
Lisp:(funcall test? (car l) a)。当调用一个函数参数或者未定义函数时使用funcall。




那简短版呢

(define rember-f
  (lambda (test? a l)
    ((null? l) (quote ()))
    ((test? (car l) a) (cdr l))
    (else (cons (car l) (rember-f test? a (cdr l))))))



若test?是eq?,(rember-f test? a l)如何工作的

若test?是eq?,(rember-f test? a l)如同rember。



若test?是eq?,(rember-f test? a l)如何工作的

这是用equal?替换了eq?的rember



现在我们有三个函数做差不多的事情

是的:
用=的rember
用equal?的rember
用eq?的rember
rember-f



而 rember-f 的行为可以向其它的那样



函数可以返回什么类型的值

表和原子



函数本身呢

可以的,只是你还不知道



你能说说(lambda (a l) ...)是什么吗

一个函数,有a和l两个参数。



(lambda (a) (lambda (x) (eq? x a)))是什么吗

这是一个函数,当a传入参数a返回函数(lambda (x) (eq? x a)),a就是那个参数。



这就是所谓的"Curry-ing?"

感谢您,Moses Schonfinkel (1889-1942)



这不叫"Schonfinkel-ing"

感谢您,Haskell B.Curry (1900-1982)



用(define ...)给函数定义一个函数名

(define eq?-c
  (lambda (a)
    (lambda (x)
      (eq? x a))))

注:
Lisp:
(defun eq?-c (a)
  (function
    (lambda (x)
      (eq x a))))



(eq?-c k)是什么,其中k是 salad

这是一个函数,把x作为函数参数测试其是否与salad是eq?相等的



所以让我们用(define ...)为它定义一个函数名
(define eq?-salad (eq?-c k)),其中k是salad

注:
Lisp:
(setq eq?-salad (eq?-c 'salad))。使用setq定义的函数能够用funcall调用。

好。



(eq?-salad y)其中y是salad

#t



注:
Lisp:
(funcall eq?-salad y),因为eq?-salad还没有定义




(eq?-salad y)其中y是tuna

#f




我们需要给 eq?-salad定义吗

不需要,我们可以这样((eq?-c x) y),其中x是salad,y是tuna

注:
Lisp:
(funcall (eq?-c x) y),因为(eq?-c x)是为定义的函数




现在重写 rember-f 为一个参数test?的函数,返回一个如同用eq?替换test?的rember。

(define rember-f
  (lambda (test?)
    (lambda (a l)
      (cond
        ((null? l) (quote ()))
        ((test? (car l) a) (cdr l))
        (else (cons (car l) ...))))))

一个好开端



请用你自己的话描述(rember-f test?)的结果,其中test?是eq?

结果是一个带两个参数a,l的函数。它比较表和a,并且删除第一个出现的a。



给(rember-f test?)的返回值起个名字,其中test?是eq?

(define rember-eq? (rember-f test?)),其中test?是eq?



(rember-eq? a l)是什么,其中a是tuna,l是(tuna salad is good)


(salad is good)




若test?是eq?,我们需要把(rember-f test?)其名为 rember-eq?吗

不需要,我们可以直接 ((rember-f test?) a l),其中test?是eq?,a是ntua,l是(tuna salad is good)。



现在完成 rember-f 中的(cons (car l) ...)行让 rember-f 能够运行

(define rember-f
  (lambda (test?)
    (lambda (a l)
      (cond
        ((null? l) (quote ()))
        ((test? (car l) a) (cdr l))
        (else (cons (car l) ((rember-f test?) a (cdr l))))))))




((rember-f eq?) a l)是什么,其中a是tuna,l是(shrimp salad and tuna salad)

(shrimp salad and salad)




((rember-f eq?) a l)是什么,其中a是eq?,l是(equal? eq? eqan? eqlist? eqpair?)

(equal? eqan? eqlist? eqpair?)




现在,把insertL改为insertL-f,方法可把rember改为rember-f一样。

(define insertL-f
  (lambda (test?)
    (lambda (new old l)
      ((null? l) (quote ()))
      ((test? (car l) old) (cons new (cons old (cdr l))))
      (else (cons (car l) ((insertL-f test?) new old (cdr l)))))))



作为练习,再写出insertR

(define insertR-f
  (lambda (test?)
    (lambda (new old l)
      ((null? l) (quote ()))
      ((test? (car l) old) (cons old (cons new (cdr l))))
      (else (cons (car l) ((insertR-f test?) new old (cdr l)))))))



insertR-f 和 insertL-f 相似吗

只有中间的一点点不一样



你能写出既能在左边插入又能在右边插入的函数insert-g吗

如果你能,给自己来点咖啡蛋糕放松放松吧!不能也无所谓,别放弃,等下你就能看到了。




哪一片段不一样

第二行稍有不同。在insertL中是
((eq? (car l) old) (cons new (cons old (cdr l))))
但是在insertR中是
((eq? (car l) old) (cons old (cons new (cdr l))))



请描述出来!

我们这样说:
“两个函数cons old和new到cdr l上的顺序不一样。




所以我们怎样消除差别

你大概猜出来了:h传入一个函数来描述适当的cons方式。



定义一个 seqL 函数用来:获取三个参数,第二个参数cons到第三个参数上,第一个参数cons到前面的结果上。

(define setL
  (lambda (new    old l)
    (cons new (cons old l))))



那下边这个呢
define setR
  (lambda (new    old l)
    (cons old (cons new l))))

定义一个 seqL 函数用来:获取三个参数,第一个参数cons到第三个参数上,第二个参数cons到前面的结果上。




你知道为什么我们写这两个函数吗

因为他们表达出了insertL和insertR的不同之处



试试用一个参数seq写出函数 insert-g,其中在seq为seqL时返回insertL,在seq是seqR时,返回insertR

(define insert-g
  (lambda (seq)
    (lambda (new old l)
      (cond
        ((null? l) (quote ()))
        ((eq? (car l) old) (seq new old (cdr l)))
        (else (cons (car l) ((insert-g seq) new old (cdr l))))))))




现在用 insert-g 定义 insertL

(define insertL (insert-g seqL))



还有 insertR

(define insertR (insert-g seqR))



两个函数有什么异样的

之前我们可能会这样写 (define insertL (insert-g seq)),其中seq是seqL,以及(define insertR (insert-g seq)),其中seq是seqR。

但是对于对函数传递参数,“其中...”是不需要的。



需要给seqL和seqR命名吗

不必,我们直接在定义里传递它们。



再次定义 insertL,这次不要用seqL

(define insertL
  (insert-g
    (lambda (new old l)
      (cons new (cons old l)))))

笔记:就是把seqL的定义直接替换(define insertL (insert-g seqL))中的seqL。



这个更好点吗

是的,因为你不需要记住许多名字。你可以
(rember func-name "your-mind"),其中 func-name是seqL



你还记得 subst 的定义吗

(define subst
  (lambda (new old l)
    (cond
      ((null? l) (quote ()))
      ((eq? (car l) old) (cons new (cdr l)))
      (else (cons (cdr l) (subst new old (cdr l)))))))



这个看起来类似吗

是的,就像 insertL 和 insertR。仅仅cond的第二行不一样。



为subst定义一个类似seqL或者seqR的函数

(define seqS
  (lambda (new old l)
    (cons new l)))




现在用 insert-g 定义subst

(define subst (insert-g seqS))




那你认为yyy是什么
(define yyy
   (lambda (a l)
      ((insert-g seqrem) #f a l))
其中
(define seqrem
  (lambda (new old l) l))

Surprise!这是我们的老朋友 rember。
提示:一步步推导(yyy a l),其中a 是 sausage,l是(pizza with sausage and bacon)
#f是其什么作用的?

笔记:首先 (insert-g seqrem)返回的是一个函数。该函数输入new,old,l三个参数,输出时当发现l中的某个元素与old相同就跳过,恰好与rember的作用类似,只是new参数多余了。于是当定义yyy函数时,对(insert-g seqrem)函数带入的三个参数中,第一个是与函数作用无关的,可以是任意的,取个#f也无妨。

各位看官就自己判断,个人认为这样没错。




领略到了抽象的力量了吧

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
从通用模式抽象出新的函数。
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;




我们之前见到过一些相像的函数吗

有的



还记得第六章的 value 吗

(define value
  (lambda (nexp)
    (cond
      ((atom? nexp) nexp)
      ((eq? (car (cdr nexp)) (quote +)) (+ (value (car nexp)) (car (cdr (cdr nexp)))))
      ((eq? (car (cdr nexp)) (quote ×)) ('× (value (car nexp)) (car (cdr (cdr nexp)))))
      (else ('^ (value (car nexp)) (car (cdr (cdr nexp))))))))



看出相似性了没

最后三行除了+,×,^之外都一样。




你能写出一个 atom-to-function 吗,要满足下面的条件:
1.一个参数x
2.如果(eq? x (eqote +)) 返回函数+
如果(eq? x (eqote ×)) 返回函数×
否则返回函数^

(define atom-to-function
  (lambda (x)
    (cond
      ((eq? x(quote +)) +)
      ((eq? x(quote +)) ×)
      (else ^))))



(atom-to-function (operator nexp)) 是什么,其中 nexp是 (+5 3)

函数+,而不是原子+。




你能用 atom-to-function 写出cond-两行的value吗

当然
(define (nexp)
  (lambda (nexp)
    (cond
      ((atom? nexp) nexp)
      (else
       ((atom-to-function (operator nexp)) (value (1st-sub-exp nexp)) (value (2nd-sub-exp nexp)))))))





这个版本是不是比第一个版本简短多了?

是的,但是都OK。我们没改变含义。



来一个苹果怎么样

One a day keeps the doctor away。每天坚持吃个苹果,永远无需大夫看我。



下面是 multirember
(define multirember
  (lambda (a lat)
    (cond
      ((null? lat) (quote ()))
      ((eq? a (car lat)) (multirember a (cdr lat)))
      (else (cons (car lat) ((multirember test?) a (cdr lat)))))))
写一个函数 multirember-f

没问题。
(define multirember
  (lambda (test?)
    (lambda (a lat)
      (cond
        ((null? lat) (quote ()))
        ((eq? a (car lat)) ((multirember test?) a (cdr lat)))
        (else (cons (car lat) (multirember a (cdr lat))))))))




((multirember-f test?) a lat) 是什么,其中test是 eq?,a是tuna,lat是 (shrimp salad tuna salad and tuna)

(shrimp salad salad and)




难道不是easy的很吗

是啊



用 multirember-f 定义 multirember-eq?

(define multirember-eq?
  (multirember-f test?)




我们真的需要给 multirember-f 传递tuna吗

当 multirember-f 访问lat中所有元素时,它总是寻找tuna



当 multirember-f 访问lat时test?改变吗

不会,test?一直是eq?,如同a总是tuna。



我们能把a和test?合并吗

其实,test?可以是一个参数,与tuna比较



怎么做

新参数test?取得一个参数并把它同tuna比较



下面是一种写法

(define eq?-tuna
  (eq?-c k))
其中k是tuna,你能想出一个其他方法来表示吗

下面是另一种方法
(define eq?-tuna
  (eq?-c (quote tuna)))



你见过包含原子的定义吗

见过,0,(quote ×),(quote +),还有更多



或许我们现在该写一个与 multirember-f 类似的 multiremberT 函数。 multiremberT用 eq?-tuna函数和lat来计算,而不是输入参数test?返回一个函数。

这个没什么难度

(define multiremberT
  (lambda (test? lat)
    (cond
      ((null? lat) (quote ()))
      ((test? (car lat)) (multiremberT test? (cdr lat)))
      (else (cons (car lat) (multiremberT test? (cdr lat)))))))



(multiremberT test? lat),其中test?是eq?-tuna,lat是(shrimp salad tuna salad and tuna)

(shrimp salad salad and)



简单吗

不算难



那这个呢
(define multirember&co
  (lambda (a lat col)
    (cond
      ((null? lat) (col (quote ()) (quote ())))
      ((eq? (car lat) a) (multirember&co a (cdr lat) (lambda (newlat seen)
                                                       (col newlat
                                                            (cons (car lat) seen)))))
      (else (multirember&co a (cdr lat) (lambda (newlat seen) (col (cons (car lat) newlat)
看起来好复杂!



这个看起来简单点:
(define a-friend
  (lambda (x y)
    (null? y)))

是的,这个很简单。这个函数输入两个参数并查询第二个参数是否为空表,而忽视第一个参数。




(multirember&co a lat col)是什么,其中a是tuna,lat是(strawberries tuna and swordifish),col是a-friend

这个可不简单。



所以让我们试试简单点的例子吧。(multirember&co a lat col),其中a是tuna,lat是(),col是 a-friend

#t真,因为给的参数得到第一个分支查询, a-friend保证第二个参数为空。



笔记:这一章稍有难度,于是使用 DrRacket 的调试功能来分析。但是ubuntu自带的版本5.1.3的 DrRacket 有bug,用了一段时间UI就一直花下去了,而且调试功能一直不能用。还是在官网下了最新版5.3,调试功能终于可以用了。现在可以方便的细致分析lambda这一章后边的代码了。



(multirember&co a lat col)是什么,其中a是tuna,lat是(tuna),lat是 a-friend

multirember&co 查询(eq? (car lat) (quote tuna)),其中lat是(tuna)。然后递归()。



multirember&co自然递归的其它参数是什么

第一个参数时tuna。第三个参数是一个新的函数



第三个参数名是什么

col



你知道现在col表示什么吗

col是“collector”的简称,有时称为“continuation”连续。



这是新的collector
(define new-friend
  (lambda (newlat seen)
    (col newlat
      (cons (car lat) seen))))
其中(car lat)是tuna,col是 a-friend,你把这个定义稍稍改写一下吗

(define new-friend
  (lambda (newlat seen)
    (col newlat
      (cons (quote tuna) seen))))



我们能在这个定义里把col替换为 a-friend 吗

可以:
(define new-friend
  (lambda (newlat seen)
    (a-friend newlat
      (cons (quote tuna) seen))))
笔记:就是cond第二个分支的那个lambda匿名函数




现在呢?

multirember&co 查找出(null? lat)是真,也就是它把collector作用在了两个空表上。




这是哪个collector?

new-friend



a-friend 与 new-friend有何不同

new-friend使用 a-friend 查询空表,还有值(cons (quote tuna) (quote ()))




原来那个 collector 对这个参数做什么了

返回#f,因为第二个参数是(tuna),不是空表。





==================================================
笔记:在DrRacket中调试上边这个例子
(define a 'tuna)
(define lat '(tuna))
(define a-friend
  (lambda (x y)
    (null? y)))

(define multirember&co
  (lambda (a lat col)
    (cond
      ((null? lat) (col (quote ()) (quote ())))
      ((eq? (car lat) a) (multirember&co a (cdr lat) (lambda (newlat seen)
                                                       (col newlat (cons (car lat) seen)))))
      (else (multirember&co a (cdr lat) (lambda (newlat seen)
                                          (col (cons (car lat) newlat) seen)))))))

(multirember&co a lat a-friend)
点菜单栏的“调试”按钮,以后一直“step”,下面用>表示那个绿色的指示运行到哪里的三角形

>指示cond时,
stack栏是
(cond ...)
(multirember&co ...)

Variables栏是
a => tuna
lat => (tuna)
col => #<procedure:a-friend>


点"step"......



>指示第二个分支的(cdr lat)之后,递归了,跳回指示cond
stack栏是
(cond ...)
(multirember&co ...)

Variables栏是
a => tuna
lat => ()
col => #<procedure:...e/multirember&co:11:53>
就是说col现在是位于11行,第二个分支中定义的lambda匿名函数而不是 a-friend了。



点"step"......



>指示(col newlat (cons (car lat) seen))
stack栏是
(col ...)
(multirember&co ...)

Variables栏是
newlat => ()
seen => ()
a => tuna
lat => (tuna)
col => #<procedure:a-friend>
这个lat怎么又是(tuna)了呢?其实它更本就没变。当进入递归时
((eq? (car lat) a) (multirember&co a (cdr lat) (lambda (newlat seen)
                                                       (col newlat (cons (car lat) seen)))))
注意这几个lat还都是(tuna),只有 multirember&co的第二个参数(cdr lat)减去了第一个原子。
从variables知道(col newlat (cons (car lat) seen)))))是#f,因为newlat是空表(其实没用),(cons (car lat) seen)是(tuna),不是空表。
笔记end
==================================================





(multirember&co a lat a-friend)是什么值,其中a是tuna,lat是(and tuna)

这次 multirember&co 对另一个friend递归
(define latest-friend
  (lambda (newlat seen)
    (a-friend (cons (quote and) newlat) seen)))



这次这个 multirember&co 的递归的值是什么

#f,因为(a-friend ls1 ls2)是#f,其中ls1是(and),ls2是(tuna)。




(multirember&co a lat f)做些什么

查询lat中的每一个atoma原子看和a是否eq?。不同的放在ls1,相同的放在ls2,最后,它确定(f ls1 ls2)的值。

笔记:ls1就是那个newlat,seen就是ls2。准确的的说最后ls1是当那个(car lat)不是a时不断(cons (car lat) newlat)得到的一个表;ls2是当(car lat)是a时,不断(cons (car lat))的一个表。(f ls1 ls2)为真值表示没有把a找到并放入ls2,最后所有的原子都到了newlat中,seen一直为空;假表示ls2中找到了所有的a,它是非空了。函数 multirember&co在递归的三个分支中使用了不同的col函数去处理,最后递归的结果是把lat分成两个lat,以是否是a为条件。其中每深一次的递归 multirember&co 代入的col都是比上一层的递归多一层col。



最后的问题:(multirember&co (quote tuna) ls col)的值是什么,其中ls是(strawberries tuna and swordifish),col是
(define last-friend
  (lambda (x y)
    (length x)))

3,因为ls包含三个不是tuna的原子,所以 last-friend作用于(strawberries and swordifish)和(tuna)

==================================================
笔记:使用DrRacket的调试功能step一步步查看运行过程可以容易的理解代码,这里就不细细的贴上来了。因为scheme是 S-expression表达式,于是这次直接展开scheme代码来推导。定义的 last-friend 作为参数col传入 multirember&co。在(null? ls)为真值,即所有的原子都查完了的时候才依是否是a把原子分到了x和y两个表中。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
查询l的第一个原子 strawberries 时,递归第三分支
(else (multirember&co a (cdr lat) (lambda (newlat seen)
                                          (col (cons (car lat) newlat) seen)))))))
输入参数a是 tuna
输入参数lat是ls,即(strawberries tuna and swordifish)
输入参数col是 last-frined

下一次递归时输入的a是 tuna
下一次递归时输入的ls是(cdr lat)即(tuna and swordifish)
下一次递归时输入的col是
(lambda (newlat seen)
        (col (cons (car lat) newlat) seen))
展开即
(lambda (newlat seen)
        (last-frined (cons strawberries newlat) seen))
可以暂时记为函数col-1,方便分析,注意 last-frined函数,因此col-1是2阶函数
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
查询l的第二个原子 tuna 时,递归第二分支
((eq? (car lat) a) (multirember&co a (cdr lat) (lambda (newlat seen)
                                                       (col newlat (cons (car lat) seen)))))
输入参数a是 tuna
输入参数lat是(cdr ls),即(tuna and swordifish)
输入参数col是
(lambda (newlat seen)
        (col (cons (car lat) newlat) seen))

下一次递归时输入的a是 tuna
下一次递归时输入的ls是(cdr lat)即(and swordifish)
下一次递归时输入的col是
(lambda (newlat seen)
        (col newlat (cons (car lat) seen)))
其中lat,col是这次递归输入参数,展开得到
(lambda (newlat seen)
        (col-1 newlat (cons tuna seen)))
可以暂时记为函数col-2,方便分析,注意col-1是2阶函数,于是col-2是3阶函数
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
查询l的第三个原子 tuna 时,递归第三分支
(else (multirember&co a (cdr lat) (lambda (newlat seen)
                                          (col (cons (car lat) newlat) seen)))))))
输入参数a是 tuna
输入参数lat是(cdr lat),即(swordifish)
输入参数col是col-2,
(lambda (newlat seen)
        (col-1 newlat (cons tuna seen)));col-1,col-2的含义见之前的递归分析

下一次递归时输入的a是 tuna
下一次递归时输入的ls是(cdr lat)即(tuna and swordifish)
下一次递归时输入的col是
(lambda (newlat seen)
        (col (cons (car lat) newlat) seen))
其中lat,col是这次递归输入参数,展开得到
(lambda (newlat seen)
        (col-2 (cons and newlat) seen))
可以暂时记为函数col-3,方便分析,注意col-2是3阶函数,于是col-3是4阶函数
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
查询l的第四个原子 swordifish 时,递归第三分支
(else (multirember&co a (cdr lat) (lambda (newlat seen)
                                          (col (cons (car lat) newlat) seen)))))))
输入参数a是 tuna
输入参数lat是(cdr lat),即(swordifish)
输入参数col是col-3,
(lambda (newlat seen)
        (col-2 (cons and newlat) seen));col-1,col-2,col-3的含义见之前的递归分析

下一次递归时输入的a是 tuna
下一次递归时输入的ls是(cdr lat)即()
下一次递归时输入的col是
(lambda (newlat seen)
        (col (cons (car lat) newlat) seen))
其中lat,col是这次递归输入参数,展开得到
(lambda (newlat seen)
        (col-3 (cons swordifish newlat) seen))
可以暂时记为函数col-4,方便分析,注意col-3是4阶函数,于是col-3是5阶函数
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
查询l的第四个原子 swordifish之后,即空表()时,递归第一分支
((null? lat) (col (quote ()) (quote ())))
输入参数a是 tuna
输入参数lat是(cdr lat),即()
输入参数col是col-4,
(lambda (newlat seen)
        (col-4 (cons swordifish newlat) seen));col-1,col-2,col-3,col-4的含义见之前的递归分析

下一次递归时...没有下次递归了,这是最后一次,其中col是col-4,代入col-4展开得到
((lambda (newlat seen)
   (col-3 (cons swordifish newlat) seen)) (quote ()) (quote ()))

(col-3 (quote (swordifish)) (quote ()))
代入col-3 展开得到
((lambda (newlat seen)
        (col-2 (cons and newlat) seen)) (quote (swordifish)) (quote ()))

(col-2 (quote (and swordifish)) (quote ()))
代入col-2 展开得到
((lambda (newlat seen)
   (col-1 newlat (cons tuna seen))) (quote (and swordifish)) (quote ()))

(col-1 (quote (and swordifish)) (quote (tuna)))
代入col-1 展开得到
((lambda (newlat seen)
            (last-friend (cons strawberries newlat) seen)) (quote (and swordifish)) (quote (tuna)))

(last-friend (quote (strawberries and swordifish)) (quote (tuna)))
代入last-friend得到
3
注意:虽然 last-friend 是在最内层,但是它的输入参数完全依赖外层的函数,并且每一个内层的输入都依赖它的外层,因此 last-frined函数最后才执行。

scheme太数学了!
end笔记
==================================================





是的!

这可真是奇怪的食物,不过我们已经见识过外国菜了。



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
第十戒

用函数,一次collect不止一个值
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



下面这个是老朋友
(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)))))))))

还记得 multiinsertR 吗
当然了
(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)))))))))




现在试试写个 multiinsertLR
提示: 如果oldL和oldR是不同的,multiinsertLR 把 new 插入到 oldL 的左边和 oldR的右边



下面这个把两个函数和在一起了
(define multiinsertLR
  (lambda (new oldL oldR lat)
    (cond
      ((null? lat) (quote ()))
      ((eq? (car lat) oldL) (cons new (cons oldR (multiinsertLR new oldL oldR (cdr lat)))))
      ((eq? (car lat) oldR) (cons oldR (cons new (multiinsertLR new oldL oldR (cdr lat)))))
      (else (cons (car lat) (multiinsertLR new oldL oldR (cdr lat)))))))



multiinsertLR&co 之与 multiinsert 如同multirember&co 之与 multirember

这意味着 multiinsertLR&co 需要比 multiinsertLR 更多的参数的参数?



是的。参数是什么呢?

是一个collector函数



当 multiinsertLR&co 完成,它会对new lat,对左插入的数,右插入的数使用col。你能写出 multiinsertLR&co 的要略吗

(define multiinsertLR&co
  (lambda (new oldL oldR lat col)
    (cond
      ((null? lat) (col (quote () 0 0)))
      ((eq? (car lat) oldL) (multiinsertLR&co new oldL oldR (cdr lat) (lambda (newlat L R) ...)))
      ((eq? (car lat) oldR) (multiinsertLR&co new oldL oldR (cdr lat) (lambda (newlat L R) ...)))
      (else (multiinsertLR&co new oldL oldR (cdr lat) (lambda (newlat L R) ...))))))



为什么当(nul? lat)为真时,col作用于(quote () 0 0)

空lat既没有oldL也也没有oldR。这表示oldL和oldR发现了0次, multiinsertLR 当lat为空时返回的是 ()。



那么(multiinsertLR&co (quote  cranberries) (quote fish) (quote chips) (quote ()) col) 的值是什么

是(col (quote ()) 0 0),但是因为我们步子到col是什么,还无法确定它。



当(car lat) 既不等于 oldL也不等于oldR是, multiinsertLR&co 对三个参数作用新的collctor对吗

是的,第一个参数是lat, 原本是 multiinsertL 作用(cdr lat), old, 和old。第二个和第三个是对应插入olL左边和oldR右边的次数。



然后,因仅当出现oldL或者oldR才复制,所以 multiinsertLR&co 会对(cons (car lat) newlat)使用col对吗

是的,没错。所以我们知道了最后一个分支情况的collector了
(lambda (newlat L R)
  (col (cons (car lat) newlat) L R))



为什么col的第二个和第三个参数是L和R

如果(car lat) 即不是oldL也不是oldR,我们不需要插入任何新的元素。所以L和R对(cdr lat)和lat都是对的。



下面这个是我们现在得到的,新添了一个collector
(define multiinsertLR&co
  (lambda (new oldL oldR lat col)
    (cond
      ((null? lat) (col (quote () 0 0)))
      ((eq? (car lat) oldL) (multiinsertLR&co new oldL oldR (cdr lat) (lambda (newlat L R)
                                                                        (col (cons new (cons oldL newlat) (add1 L) R)))))
      ((eq? (car lat) oldR) (multiinsertLR&co new oldL oldR (cdr lat) (lambda (newlat L R) ...)))
      (else (multiinsertLR&co new oldL oldR (cdr lat) (lambda (newlat L R)
                                                        (col (cons (car lat) newlat) L R)))))))



未完成的那个collector和新添加的collector很类似。只是对R加1,而不是对L加1;另外,是cons old到 cons new newlat的结果上。



那你能填写省略号吗

当然了, 最后这个 collector 是
(lambda (new L R)
  (col (cons oldR (cons new newlat)) L (add1 R)))



(multiinsertLR&co new oldL oldR lat col)的值是什么,其中 new是salty,oldL是fish,oldR是chips,lat是(chips and fish or fish and chips)

是(col newlat 2 2)的值,其中newlat是(chips salty and salty fish or salty fish and chips salty)




这个对身体健康吗(译注:指的是chips salty and salty fish or salty fish and chips salty)

盐太多了。或许点心甜一些。



还记得什么是 *-函数吗

当然了,所有的*-函数都能对各种list
——空表
——con 原子到list表上的lsit表
——cons list表到list表上的lsit表



现在写一个函数 evens-only*。它删除网状list组成的list中所有的奇数。下面这是even?
(define even?
  (lambda (n)
    (= (× (÷ n 2) 2) n)



我们已经讲过怎么写这种函数, evens-only*只是个练习:

(define evens-only*
  (lambda (l)
    ((null? l) (quote ()))
    ((atom? (car l))
     (cond
       ((even? (car l)) (cons (car l) (evens-only* (cdr l))))
       (else (evens-only* (cdr l))))
     (else (cons (evens-only* (car l)) (evens-only* (cdr l)))))))



(evens-only* l)的值是什么,其中 l是 ((9 1 2 8) 3 10 ((9 9) 7 6) 2)

((2 8) 10 (() 6) 2)



如果l是((9 1 2 8) 3 10 ((9 9) 7 6) 2),那么l中奇数的总和是多少

9+1+3+9+9+7=38



如果l是((9 1 2 8) 3 10 ((9 9) 7 6) 2),那么l中偶数的乘积是多少

2×8×10×6×2=1920



你能写出一个 evens-only*&co 函数吗,它把奇数从参数中删除来构建网状的list表,同时把所有偶数相乘,奇数相加

这可满是star哦!



下面这里是个要略。你能解释(evens-only*&co (car l) ...) 是干什么的吗

(define evens-only*&co
  (lambda (l col)
    (cond
      ((null? l) (col (quote ()) 1 0))
      ((atom? (car l)) (cond
                         ((even? (car l)) (evens-only*&co (cdr l) (lambda (newl p s)
                                                                    (col (cons (car l) newl) (×(car l) p) s))))
                         (else (evens-only*&co (cdr l) (lambda (newl p s)
                                                         (col newl p (+ (car l) s))))))
                       (else (evens-only*&co (car l) ...))))))

它访问(car l)中的每一个数,然后收集表中的偶数,得到偶数的乘积,奇数的和。



函数 evens-only*&co 在访问了所偶的 (car l)中的数后做什么了

如同前边的例子一样,使用collector,不过我们还没有定义它。



这个collector做了什么

用evens-only*&co访问(cdr l)中的所有偶数,然后计算了偶数的乘积,奇数的和。



这就是说那个collector大体上应该是这样的对么

(lambda (al ap as)
  (evens-only*&co (cdr l)
    ...))

是的。



当(evens-only*&co (cdr l) ...)执行完毕之后呢

那个还没定义的collector就开始执行了




(evens-only*&co (cer l) ...)的collector做了什么

它把list中car和cdr的结果cons到一起,然后做相应的乘积和求和。最后把值传给最开始输入的那个老collector
(lambda (al ap as)                  ;collector of (evens-only*&co (cer l) ...)
  (evens-only*&co (cdr l)
    (col (cons al dl) (× ap dp) (+ as ds))))
笔记:这个情形下,car l不是原子而是表,稍稍麻烦点,(car l)和(cdr l)需要分别递归得到两个部分的偶数乘积和奇数,最后对应相成和相加。




理解这些了吗

perfect!



(evens-only*&co l the-last-friend)的值是什么,其中 l是((9 1 2 8) 3 10 ((9 9) 7 6) 2), the-last-friend的定义如下:
(define the-last-friend
  (lambda (newl product sum)
    (cons sum
      (cons product newl))))

(38 1920 (2 8) 10 (() 6) 2)





哇哦!你的脑子搅起来了吧?

去吃块椒盐饼干吧,别忘了芥末哦。

 

 

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

posted @ 2012-08-26 16:47  Z.X.L  阅读(708)  评论(0编辑  收藏  举报