全部学习汇总: ​​GitHub - GreyZhang/g_SICP: learn SICP and hack lisp.​

1174_SICP学习笔记_层次结构_网络协议

    其实,关于着色的这部分的理解是很容易的。尤其是,当我们的理解建立在前面分析了append实现之后。其实,一个list的元素扩充从前面扩充会比从后面扩充容易,可以避免nil的处理。而这,也让这个模型在理解上更加容易。cons函数的第一个元素,总会成为新的数据表中的第一个元素。

1174_SICP学习笔记_层次结构_学习_02

1174_SICP学习笔记_层次结构_网络协议_03

    如果使用list来表达树的结构,那么求解这个树的节点有一个专用的函数。如果表达的含义是树,其实在处理上理解起来也是很容易的。car的结果以及cdr的结果正好是一个树的两个分支。而节点数目的计算,其实是尝试穷尽每一个节点的car以及cdr。这样,在实现count-leaves的时候,主要的递归部分就是正好是这样的描述实现。从表达上,其实null?的判断是无关紧要的,但是从防止报错的角度来讲这部分不能够少。因为我们可能遇到一个元素的list,取出来的元素信息会是nil。

1174_SICP学习笔记_层次结构_p2p_04

    (1 (2 (3 4))),这样的结果很容易得出,只需要进行反复的替代模型执行就可以。

1174_SICP学习笔记_层次结构_网络协议_05

    这个例子我在做测试尝试的时候遇到了一点问题,看起来对于这个数据结构的理解还是深度不够。我自己的尝试记录如下:

1174_SICP学习笔记_层次结构_p2p_06

    只做了2个尝试,另一个其实是相对简单的,不去做尝试了。接下来,针对自己犯错的地方再复习回顾一下。

1174_SICP学习笔记_层次结构_网络协议_07

    这样,在理解的时候得需要注意一下。其实,每一个list处理之后都会有一个隐含的nil存在。这样,再回去理解前面的内容就顺理成章了。

1174_SICP学习笔记_层次结构_lisp_08

    这个操作在理解上其实还是容易理解的。

1174_SICP学习笔记_层次结构_p2p_09

    这个测试的结果跟思考的结果也是一致的。

1174_SICP学习笔记_层次结构_学习_10

    其实,这个处理的逻辑前面大概看过一个类似的框架,也就是实现计算树的叶子节点的那部分。同样的框架,做一个简单的修改就可以实现这个功能。

(define (fringe x)

  (cond ((null? x) (display ""))

        ((not (pair? x))

         (display " ")

         (display x))

        (else

         (fringe (car x))

         (fringe (cdr x)))))

    运行结果如下:

1174_SICP学习笔记_层次结构_网络协议_11

    还剩下一个练习,大概看懂了其中的含义,跳过不看了。又一次偷懒,惭愧惭愧!

1174_SICP学习笔记_层次结构_p2p_12

    这个是关于树的一个批量处理的实现,使用的软件的设计结构还是前面看到的那个结构。按照老套路,接下来应该还是要实现一个对此功能的高阶抽象吧?

1174_SICP学习笔记_层次结构_sicp_13

    这里,没有使用新的抽象实现而是使用了map。而实现的关键其实在于递归与map的组合,在递归的过程中继续map。而map其实是一个遍历取值的过程,如此实现对整个树的每一个节点的处理。的确是很巧妙,设计的时候没有根据树来做什么新的抽象,而是直接使用了list的处理,因为树本来就是list的派生,所以相应的处理依然奏效。

    另一个需要注意的地方是这个lambda的实现,这里的确是第一次看到这种灵活度的lambda表达式。在解析的过程中,一个表达式其实是可以被解析生成不同的函数调用。

1174_SICP学习笔记_层次结构_lisp_14

    很多处理的过程跟这个过程很相似,这里的练习就是其中的一个。上面的代码其实很容易修改一下就生成完成这段处理需求的代码。

(define (square-tree tree)

  (map (lambda (sub-tree)

         (if (pair? sub-tree)

             (square-tree sub-tree)

             (* sub-tree sub-tree)))

       tree))

    以上就是修改出来的一个版本,几乎没啥逻辑上的修改。

1174_SICP学习笔记_层次结构_lisp_15

    测试的结果如上。

1174_SICP学习笔记_层次结构_lisp_16

    看起来,我已经很熟悉这个教程的玩法了。前面的猜测本以为不做了,看起来是换了个地方而已。出现在了练习当中。其实这样的设计,可以说是轻车熟路了,小改即可。

(define (tree-map p-item tree)

  (map (lambda (sub-tree)

         (if (pair? sub-tree)

             (tree-map sub-tree)

             (p-item sub-tree)))

       tree))

1174_SICP学习笔记_层次结构_p2p_17

1174_SICP学习笔记_层次结构_p2p_18

    这个联系看起来很有意思,目的是实现一个list,包含所有的输入list元素的排列组合信息。接下来分析一下这个函数现在已经有的设计。

    如果,s是一个空的list,那么直接返回空的list。否则,这个会先求解s的cdr的所有排列组合。之后,这个结果如何组合出来最终的结果呢?应该是与第一个元素进行组合。

    第一个元素的组合方式应该是: (const first-item each-item-in-rest)。而first-item是可以直接求解的,至于后面的list就是rest。上面的过程,应该通过map来进行遍历。于是,设计出来的程序如下:

(define (subsets s)

  (if (null? s)

      (list nil)

      (let ((rest (subsets (cdr s))))

        (append rest (map (lambda(x) (cons (car s) x)) rest)))))

    测试的结果:

1174_SICP学习笔记_层次结构_网络协议_19

    这个问题的解决过程其实很有意思,是一个递归的过程。而这个递归过程,其实在前面进行钱币组合的例子中就已经遇到过了。这里,仅仅是一个简单的重现。

    到此为止,其实通过简单的操作已经实现了很多树的高级操作了。最后这个例子也可以看得出来,这个处理过程的简洁效果。其实之前很多python处理的小工具中也用过这种排列组合的实现,我当时的实现真是有一些绞尽脑汁的感觉。最终,虽然也实现了需要的功能,但是相比这里看到的这种设计理念来说无疑是复杂多了。