三分's profile3fen's NotebookPhotosBlogListsMore ![]() | Help |
|
September 10 SICP 4.3.1Section 4.3.1 由于前两小节的习题需要amb-evaluator去测试,所以需要先把4.3.3中的求值器实现。 amb-eval相比之前两个求值器要复杂,郁闷的看了n遍。其中比较麻烦的事情就是succeed和fail过程传来传去,还有succeed的调用也让人一上来觉得不太适应。 所谓的succeed和fail,可以理解为运行过程中的两个分叉,之前接触的eval-apply循环、lazy-eval可以看做是全程succeed的情况。相类似的analyze-self-evaluating/analyze-quoted等过程,都只是传递succeed和fail过程而涉及不到运行的分叉。 处理if语句时,由于if-predict中可能有amb语句(可以造成fail),所以要进行判断:如果if-predict是"成功的",那么分情况考虑if-consequent或者if-alternative;若是"失败"的,就调用fail过程。书中analyze-if中的fail2只是为了在名字上与fail区分开来,实际的值也是fail,而不会存在两个不同的fail过程。 set!过程又与上面不同,由于需要消除赋值带来的副作用,所以求值assignment-value的fail过程和外层set!的fail过程是不同的,不可以像if那样直接调用过来。 个人感觉最难理解的是过程应用中的get-args过程。它的作用大概就是把apply的参数值提取出来(由于这些值都是(lambda(env succeed fail)(...)之类的东西,所以需要另写get-args而不是直接用map))。其中递归调用get-args时,增量是get-args的参数succeed的val部分。可以这么看: succeed过程实际是(lambda(args fail) (succeed args fail)),仔细观察程序可以发现,递归调用中的succeed部分是这样的:(lambda(args fail) (succeed (cons arg args) fail)),增加的那个arg就是提取出来的value。 相关的analyze-application和execute-application相对容易,理解这部分可以通过人肉追踪一个简单的过程应用来完成。比如(+ 1 (amb 2 3))。 (ps. 这里如果用Drscheme的调试功能会比较郁闷,因为绝大多数想知道的数据和过程都是(#procedure)...) 接下来的amb过程是整个求值器中最独特最独特的部分,但是原理并不复杂,有点类似analyze-sequence。 4.35 (define (an-integer-between low high) (if (> low high) (amb) (amb low (an-integer-between (+ 1 low) high)))) 4.36 按照题中的顺序,如果只用an-integer-starting-from简单代替的话,程序会取i=1, j=1,然后把k从1一直取值试下去。得不到正确结果。 所以这里需要的是一种能够遍历i、j、k的方法,有点类似第三章讲流时的那个求所有整数序对的意思,不过这里更简单一些。 (define (a-pythagorean-triple) (let ((k (an-integer-start-from 1))) (let ((i (an-integer-between 1 k))) (let ((j (an-integer-between i k))) (require (= (+ (* i i) (* j j)) (* k k))) (list i j k))))) 4.37 没做实验,想来应该是快些的,避免了很多无效的列举。虽然增加了求平方根操作,不过相比较那些无效的ijk的运算也应该算是值得了。 TrackbacksThe trackback URL for this entry is: http://3fen.spaces.live.com/blog/cns!19176E6323A90759!350.trak Weblogs that reference this entry
|
|
|