全部学习汇总: GitHub - GreyZhang/g_SICP: learn SICP and hack lisp.
其实,关于着色的这部分的理解是很容易的。尤其是,当我们的理解建立在前面分析了append实现之后。其实,一个list的元素扩充从前面扩充会比从后面扩充容易,可以避免nil的处理。而这,也让这个模型在理解上更加容易。cons函数的第一个元素,总会成为新的数据表中的第一个元素。
如果使用list来表达树的结构,那么求解这个树的节点有一个专用的函数。如果表达的含义是树,其实在处理上理解起来也是很容易的。car的结果以及cdr的结果正好是一个树的两个分支。而节点数目的计算,其实是尝试穷尽每一个节点的car以及cdr。这样,在实现count-leaves的时候,主要的递归部分就是正好是这样的描述实现。从表达上,其实null?的判断是无关紧要的,但是从防止报错的角度来讲这部分不能够少。因为我们可能遇到一个元素的list,取出来的元素信息会是nil。
(1 (2 (3 4))),这样的结果很容易得出,只需要进行反复的替代模型执行就可以。
这个例子我在做测试尝试的时候遇到了一点问题,看起来对于这个数据结构的理解还是深度不够。我自己的尝试记录如下:
只做了2个尝试,另一个其实是相对简单的,不去做尝试了。接下来,针对自己犯错的地方再复习回顾一下。
这样,在理解的时候得需要注意一下。其实,每一个list处理之后都会有一个隐含的nil存在。这样,再回去理解前面的内容就顺理成章了。
这个操作在理解上其实还是容易理解的。
这个测试的结果跟思考的结果也是一致的。
其实,这个处理的逻辑前面大概看过一个类似的框架,也就是实现计算树的叶子节点的那部分。同样的框架,做一个简单的修改就可以实现这个功能。
(define (fringe x)
(cond ((null? x) (display ""))
((not (pair? x))
(display " ")
(display x))
(else
(fringe (car x))
(fringe (cdr x)))))
运行结果如下:
还剩下一个练习,大概看懂了其中的含义,跳过不看了。又一次偷懒,惭愧惭愧!
这个是关于树的一个批量处理的实现,使用的软件的设计结构还是前面看到的那个结构。按照老套路,接下来应该还是要实现一个对此功能的高阶抽象吧?
这里,没有使用新的抽象实现而是使用了map。而实现的关键其实在于递归与map的组合,在递归的过程中继续map。而map其实是一个遍历取值的过程,如此实现对整个树的每一个节点的处理。的确是很巧妙,设计的时候没有根据树来做什么新的抽象,而是直接使用了list的处理,因为树本来就是list的派生,所以相应的处理依然奏效。
另一个需要注意的地方是这个lambda的实现,这里的确是第一次看到这种灵活度的lambda表达式。在解析的过程中,一个表达式其实是可以被解析生成不同的函数调用。
很多处理的过程跟这个过程很相似,这里的练习就是其中的一个。上面的代码其实很容易修改一下就生成完成这段处理需求的代码。
(define (square-tree tree)
(map (lambda (sub-tree)
(if (pair? sub-tree)
(square-tree sub-tree)
(* sub-tree sub-tree)))
tree))
以上就是修改出来的一个版本,几乎没啥逻辑上的修改。
测试的结果如上。
看起来,我已经很熟悉这个教程的玩法了。前面的猜测本以为不做了,看起来是换了个地方而已。出现在了练习当中。其实这样的设计,可以说是轻车熟路了,小改即可。
(define (tree-map p-item tree)
(map (lambda (sub-tree)
(if (pair? sub-tree)
(tree-map sub-tree)
(p-item sub-tree)))
tree))
这个联系看起来很有意思,目的是实现一个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)))))
测试的结果:
这个问题的解决过程其实很有意思,是一个递归的过程。而这个递归过程,其实在前面进行钱币组合的例子中就已经遇到过了。这里,仅仅是一个简单的重现。
到此为止,其实通过简单的操作已经实现了很多树的高级操作了。最后这个例子也可以看得出来,这个处理过程的简洁效果。其实之前很多python处理的小工具中也用过这种排列组合的实现,我当时的实现真是有一些绞尽脑汁的感觉。最终,虽然也实现了需要的功能,但是相比这里看到的这种设计理念来说无疑是复杂多了。