在常见的lisp

时间:2018-06-16 15:11:44

标签: tree common-lisp

我正在尝试编写一个可以在树中查找项目的函数,类似于序列的内置find函数。该调用可能看起来像(find-in-tree item tree :test test-fn :key key-fn)。 hyperspec说传递给find的项可以是任何lisp对象(即“任何Lisp数据”),但我想到的树不是通常的Lisp二元cons树。树,称为多树,将是(可能递归或点缀)原子或列表的列表。一个例子是(find-in-tree '(1 2) '(1 (2) nil (3 (1 2)) . 4) :test #'equal) => (1 2)或某些非零值。

在环顾四周时,我在http://lisptips.com/post/43404489000/the-tree-walkers-of-cl遇到了一些有趣的代码,这些代码经过适当调整后似乎适用于标准的cons树:

(defun find-in-tree (item tree &key (test #'eql))
  (catch 'find-in-tree
         (subst-if t (constantly nil) tree 
                   :key (lambda (element)
                          (when (funcall test element item)
                            (throw 'find-in-tree element))))
         nil))

但是,我不确定如何为多个树构件进行调整(或构建递归函数)。

1 个答案:

答案 0 :(得分:3)

像这样的东西。使用本地函数进行递归。从那里,一旦找到该项,就可以使用return-from从递归中逃脱。

CL-USER> (defun find-in-tree (item tree &key (test #'eql))                             
           (labels ((find-in-tree-aux (tree)                                           
                      (cond ((funcall test item tree)                                  
                             (return-from find-in-tree tree))                          
                            ((consp tree)                                              
                             (find-in-tree-aux (car tree))                             
                             (find-in-tree-aux (cdr tree))))))                         
             (find-in-tree-aux tree)))
FIND-IN-TREE                                                                           
CL-USER> (find-in-tree 3 '((2 (4 3)) 5))
3                                                                                      
CL-USER> (find-in-tree 12 '((2 (4 3)) 5))
NIL                                                                                    
CL-USER> (find-in-tree "foo" '(("bar" ("baz")) "foo") :test #'equalp)
"foo"                                                                   
CL-USER> (find-in-tree 6 '((2 (4 3 . 6)) 5))
6

CL-USER 14 > (defun find-in-tree (item tree &key (test #'eql) (key #'identity))
              (labels ((find-in-tree-aux (tree)                                   
                         (cond ((funcall test item (funcall key tree))
                                (return-from find-in-tree tree))
                               ((consp tree)
                                (find-in-tree-aux (car tree))
                                (find-in-tree-aux (cdr tree))))))
                (find-in-tree-aux tree)))
FIND-IN-TREE

CL-USER 15 > (find-in-tree "foo" '(("a" 10)
                                   (("b" 20)
                                    ("foo" 300))
                                   ("c" 40))
                           :test #'equalp
                           :key (lambda (i)
                                  (when (consp i)
                                    (first i))))
("foo" 300)

节点作为树木列表

CL-USER 1 > (defun find-in-tree (item tree &key (test #'eql) (key #'identity))
              (labels ((find-in-tree-aux (tree)                                   
                         (cond ((funcall test item (funcall key tree))
                                (return-from find-in-tree tree))
                               ((listp tree)
                                (mapc #'find-in-tree-aux tree)
                                nil))))
                (find-in-tree-aux tree)))
FIND-IN-TREE