Adolph两边

 

《On Lisp》第四章第三节图4.3中的prune函数fix

这个函数作者的原意是删除表中test位真的部分,并且表按原样返回.

作者给出的的测试用例如下:

(prune #'evenp '(1 2 (3 (4 5) 6) 7 8 (9)))

返回结果是:

(1 (3 (5)) 7 (9))

这里的(9)应为刚好被evenp判断为假,所以正常包含在列表当中了,可是当有类似(9)这样的单元素列表包含在内的特殊情况就被作者忽略了,我偶然输入了如下测试用例,起初只是为了观察嵌套列表的层数是否因递归而减少.

(prune #'evenp '((((1 2) 3 4)) 5 (6) (7) (((8 9)))))

得到如下的结果:

((((1) 3)) 5 NIL (7) (((9))))

嵌套层数未变,可是却多出了一个nil,仔细观察代码,会发现这个nil必然是多余的,如果不是多余的,这个函数可以用很容易理解的递归完成.于是下面就想着自己完善这个实用工具.

(defun prune (test tree)
  (labels ((rec (tree acc)
             (cond ((null tree) (nreverse acc))             ;4
                   ((consp (car tree))
                        (rec (cdr tree)
                             (cons (rec (car tree) nil) acc)))  ;2
                   (t (rec (cdr tree)
                           (if (funcall test (car tree))
                             acc                                ;3
                             (cons (car tree) acc)))))))
    (rec tree nil)))  ;1

首先,肯定是多了个nil元素被添加到列表中,所以我们要寻找这个nil的来源.

先看4,这个是递归函数出口,nil完全不可能来自这里,而1是入口,这里的nil明显是迭代列表.

那剩下来只有2和3了.仔细观察,发现进入t分支的时,只要是由于分支2的调用,acc便是nil,只有在3分支的下一个元素是在一个层级上,才能迭代这个层级的表项.也就是说,真正产生表项的,是分支3.既然找到了nil的源头,那问题就简单了,只要分支3返回nil,就忽略掉.

 

(defun prune (test tree)
  (labels ((rec (tree acc)
             (cond ((null tree) (nreverse acc))
                   ((consp (car tree))
                    (let ((ret (rec (car tree) nil)))    ;fixed
                      (if ret
                        (rec (cdr tree) (cons ret acc))
                        (rec (cdr tree) acc))))
                   (t (rec (cdr tree)
                           (if (funcall test (car tree))
                             acc
                             (cons (car tree) acc)))))))
    (rec tree nil)))

 

posted on 2014-05-07 21:35  Adolph两边  阅读(684)  评论(0编辑  收藏  举报

导航