《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)這樣的單元素列表包含在內的特殊狀況就被做者忽略了,我偶然輸入了以下測試用例,起初只是爲了觀察嵌套列表的層數是否因遞歸而減小.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)))
相關文章
相關標籤/搜索