【问题标题】:Finding an item in a tree in common lisp在 common lisp 中查找树中的项目
【发布时间】:2018-11-26 02:19:17
【问题描述】:

我正在尝试编写一个可以在树中查找项目的函数,类似于序列的内置 find 函数。电话可能看起来像(find-in-tree item tree :test test-fn :key key-fn)。 hyperspec 说传递给find 的项目可以是任何 lisp 对象(即“任何 Lisp 数据”),但我想到的树不是通常的 Lisp 二叉树。这棵树,称为多树,将是原子或列表的(可能是递归或点状的)列表。一个例子是(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))

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

【问题讨论】:

  • 不应该用(or (find-in-tree ... (car tree) ...) (find-in-tree ... (cdr tree) ...))吗,(搜索nil的时候有一些注意事项?)
  • 是的,它为原子修复了它,谢谢。
  • 你需要想出树的定义和一些实际的例子。否则我们必须猜测。也不清楚为什么你不想检查 CDR。只看 CAR 不会遍历树。

标签: tree common-lisp


【解决方案1】:

类似的东西。使用本地函数进行递归。一旦找到该项目,就可以使用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

【讨论】:

  • 我现在更了解如何使用辅助功能了,谢谢。但它仍然需要避免测试 cdr:(find-in-tree '((2 3)) '(1 (2 3)) :test #'equal) => ((2 3))。这似乎与上面 coredump 的评论相同。
  • @davypough 在这种情况下,return-from 从find-in-tree 退出,这是递归的第零级。这与您从每个递归调用正常返回、结果必须沿调用链向上传播以及您必须使用or 手动切断搜索的情况不同。希望这很清楚。
  • @daypough:对于 cons 树,它做正确的事。如果你有另一棵树,你需要定义一个节点是什么,它的内容是什么以及它的后继者是什么。
  • 好的,但是对树的“cons 树”解释对我来说似乎很不直观。在类似的序列操作(find '((2 3)) '(1 (2 3)) :test #'equal) => NIL 中,((2 3)) 不是序列 (1 (2 3)) 的元素,而是树 (1 (2 3)) 的元素。但是,看起来我可以通过使用 aux 函数仅测试每棵树的汽车来在树中找到(使用我的解释)。然后,封闭的 main 函数将测试输入树的项目相等性,然后将其转换为列表并将其传递给 aux?
  • @davypough 你的论点是什么?一棵树还是一棵树的列表?你需要定义你的树来摆脱混乱。顺便提一句。 cons 树是简单的二叉树,其中 cons 单元格作为节点,car 和 cdr 作为后继。这非常直观。
猜你喜欢
  • 2010-10-27
  • 1970-01-01
  • 2010-09-15
  • 1970-01-01
  • 2010-11-09
  • 2016-03-18
  • 1970-01-01
  • 2015-05-04
  • 2013-01-24
相关资源
最近更新 更多