deep-reverse SCIP-2.27
题目:以一个表为参数,返回另一个表作为值。结果表中的元素反转过来,其中的子树也反转。
例如:
输入:(list (list (list (list 2 3 99) (list 3 22 44 4) (list 6)) (list 0 2 3 7 8)) (list 9 10 11))
输出:(list (list 11 10 9) (list (list 8 7 3 2 0) (list (list 6) (list 4 44 22 3) (list 99 3 2))))
图形显示更直观,我手头没有好的工具,本来想画在稿纸上拍了传上来,但感觉太麻烦了。。。
我首先想到解决方法1如下:
(define (deep-reverse item)
(cond ((not (pair? item)) item)
((null? (cdr item)) (deep-reverse (car item)))
(else (list (deep-reverse (cdr item))
(deep-reverse (car item))))))
代码原意:
代码第二行用于递归到叶子节点时返回具体的元素值(num or nil)
因为list以nil结尾,第三行用于递归到nil前叶子节点后,去掉nil
第四、五行则是进行递归调用,并将左右节点反转。
对于输入1:(list (list 2 3) (list 4 5))
输出:(list (list 5 4) (list 3 2))
对于输入2:(list (list (list 2)))
输出:2
对于输入3:(list (list 2 3 4 5 ) (list 6 7 8))
输出:(list (list (list 8 7) 6) (list (list (list 5 4) 3) 2))
错误分析:
对于输入2,(cdr item)一直为nul,不断求(car item)最后返回2
对于输入3,当list中元素超过2个时,(list (deep-reverse (cdr item)) (deep-reverse (car item)))会将最后两个元素组合为list,并不断与前面的每个元素组合为list。
总结:方法1属于针对(list (list 2 3) (list 4 5))这种情况进行的特殊处理,结果遇到其它list类型就完全失效了。
我苦思冥想了好几天,也没找到好的方法--脑袋确实比较笨。。。
想不通的地方是:对于list A: (list 2 3 4)与list B: (list (list 2 3) (list 4)),它们都会递归得到(list 4)。
但list A中(list 4)应该与前面的叶子组合为一个list,而list B中(list 4)则应该与前面的list组合为一个复合list。
如何区分这两中不同的形式?
原来用list连接(deep-reverse (cdr item))与(deep-reverse (car item)),这样的做法也不可取,那应该采取什么形式呢?
我在纸上笔画了好多天,完成了一个比较丑陋的解法(单纯觉得代码的形式不舒服),方法2如下:
(define nil '())
;将list a与b连接起来,b可以是list也可以不是
(define (append a b)
(if (null? a)
(if (pair? b)
(list b)
(cons b nil))
(cons (car a) (append (cdr a) b))))
;处理反转过程中的nil结束符,nil的出现也意味着list的创建
(define (list-nil x y)
(if (null? x)
(list y)
(append x y)))
(define (deep-reverse item)
(if (not (pair? item))
item
(list-nil (deep-reverse (cdr item))
(deep-reverse (car item)))))
PS:在思考这个问题的过程中还想到一个比较诡异的方法,但下面的方法3没有实现:
;直接将所有元素反转,然后再去掉前面的nil
(define (deep-reverse item)
(cond ((not (pair? item)) item)
(else (list (deep-reverse (cdr item))
(deep-reverse (car item))))))
;(define (del-front-nil item)
; (if (= nil (car item))
; (del-front-nil (cdr item))))
思路是:先不管三七二十一,将所有的叶子反转,包括结束符nil,然后再构造一个函数去掉每个子树最前面的nil,但是去掉nil的函数非常难写,我还没完成。。。所以在这里仅提出这样一个思路吧。
不知道这道题有其它解法不,看起来更漂亮。
SICP的习题答案可以参考如下:
http://eli.thegreenplace.net/category/programming/lisp/sicp/
里面这道题的解法写得也不是很好看。。。