這個函數做者的原意是刪除表中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)))