这个函数做者的原意是删除表中test位真的部分,而且表按原样返回.函数
做者给出的的测试用例以下:工具
(prune #'evenp '(1 2 (3 (4 5) 6) 7 8 (9)))
返回结果是:测试
(1 (3 (5)) 7 (9))
这里的(9)应为恰好被evenp判断为假,因此正常包含在列表当中了,但是当有相似(9)这样的单元素列表包含在内的特殊状况就被做者忽略了,我偶然输入了以下测试用例,起初只是为了观察嵌套列表的层数是否因递归而减小.spa
(prune #'evenp '((((1 2) 3 4)) 5 (6) (7) (((8 9)))))
获得以下的结果:code
((((1) 3)) 5 NIL (7) (((9))))
嵌套层数未变,但是却多出了一个nil,仔细观察代码,会发现这个nil必然是多余的,若是不是多余的,这个函数能够用很容易理解的递归完成.因而下面就想着本身完善这个实用工具.blog
(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明显是迭代列表.class
那剩下来只有2和3了.仔细观察,发现进入t分支的时,只要是因为分支2的调用,acc即是nil,只有在3分支的下一个元素是在一个层级上,才能迭代这个层级的表项.也就是说,真正产生表项的,是分支3.既然找到了nil的源头,那问题就简单了,只要分支3返回nil,就忽略掉.test
(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)))