使用Data.Tree.Zipper遍历玫瑰树

时间:2013-10-09 23:07:47

标签: haskell tree

我有一个玫瑰树结构,我用Data.Tree表示。树中的每个节点都标有(x,y)坐标。我需要实现一种方法,在树中找到最接近给定查询坐标的节点,并将子节点添加到该节点。

我设想将其分为两个操作:

  1. 遍历树以查找最接近给定查询坐标的节点

  2. 获取先前遍历中找到的节点,并添加一个标有上述查询坐标的子项

  3. 我能想到的唯一方法是使用Data.Tree.Zipper在步骤1中遍历树,然后在步骤2中使用该zipper在特定位置插入节点。

    我有两个问题:

    • 这是解决问题的有效方法吗?

    • 如果是这样,我如何使用Data.Tree.Zipper中的函数来实现上面的步骤1?我发现树遍历很难实现,因为它需要两个维度的递归:深度和广度。

2 个答案:

答案 0 :(得分:3)

您不需要拉链来进行简单的树遍历。

import Data.Foldable (minimumBy)
import Data.Function (on)
import Data.Tree

addPt :: (Eq a, Ord b) => (a -> a -> b) -> a -> Tree a -> Tree a
addPt dist p t = down t
  where
    down (Node a xs)
      | a == closest = Node a (Node p []:xs)
      | otherwise    = Node a (map down xs)
    closest = minimumBy (compare `on` dist p) t

如果minimumBy返回的最近点重复,则会多次添加该点,并且可以稍微提高效率,因为down总是完全遍历树,即使它找到了元素早。要解决这两个问题,您可以编写一个函数,依次探索每个分支,返回(可能是增强的)分支加上,例如Bool,以指示是否添加了该点。另一方面,addPt自然是高度可并行化的(模块化在Data.Tree中使用链接列表,并用并行版本替换minimumBy)如果您尝试通过顺序化来节省工作,这将丢失它。在连续的情况下,使用拉链肯定会更有效。

答案 1 :(得分:2)

以下是如何在不假设树的布局的情况下实现步骤1的方法。为简单起见,我将选择最小节点,而不是最小化某些功能的节点,但修改这个想法并不难。出于某种原因,rosezipper没有提供拉链所有孩子的操作,所以我们必须先实现。一旦我们完成了这个,这个想法很简单:递归孩子,然后选择当前位置或递归结果的最小值。

import Data.List
import Data.Ord
import Data.Tree
import Data.Tree.Zipper

childrenAsList :: TreePos Full a -> [TreePos Full a]
childrenAsList = go . children where
    go z = case nextTree z of
        Nothing -> []
        Just z  -> z : go (nextSpace z)

minZipper :: Ord a => Tree a -> TreePos Full a
minZipper = go . fromTree where
    go z = minimumBy (comparing (rootLabel . tree))
                     (z:map go (childrenAsList z))

你当然不需要使用拉链来做有效的事情,但它肯定是解决问题的一种合理而好的方法。与双遍历方法相比,这种方法的一个优点是它应该具有最大共享。