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/

里面这道题的解法写得也不是很好看。。。