我正在尝试编写一个可以在树中查找项目的函数,类似于序列的内置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))
但是,我不确定如何为多个树构件进行调整(或构建递归函数)。
答案 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