收集有关现有树状数据的数据

时间:2015-11-09 19:36:33

标签: haskell tree traversal

假设我们已经存在树状数据,我们想要添加有关每个节点深度的信息。我们怎样才能轻松实现这一目标呢?

Data Tree = Node Tree Tree | Leaf

对于每个节点,我们希望以恒定的复杂度了解它的深度。我们有来自外部模块的数据,因此我们有如上所示的信息。现实生活中的例子是外部HTML解析器,它只提供XML树,我们想收集数据,例如每个节点包含多少个超链接。

为遍历树和收集数据创建了功能语言,应该有一个简单的解决方案。

明显的解决方案是创建并行结构。我们可以做得更好吗?

3 个答案:

答案 0 :(得分:4)

我从Chris Okasaki的精彩Purely Functional Data Structures中学到的标准技巧是缓存每个节点上昂贵操作的结果。 (也许这个技巧在Okasaki的论文之前就已经知道了;我不知道。)你可以提供智能构造函数来为你管理这些信息,这样构建树就不必痛苦了。例如,当昂贵的操作是深度时,您可以写:

module SizedTree (SizedTree, sizedTree, node, leaf, depth) where

data SizedTree = Node !Int SizedTree SizedTree | Leaf

node l r = Node (max (depth l) (depth r) + 1) l r
leaf = Leaf

depth (Node d _ _) = d
depth Leaf = 0

-- since we don't expose the constructors, we should
-- provide a replacement for pattern matching
sizedTree f v (Node _ l r) = f l r
sizedTree f v Leaf = v

构造SizedTree在每个节点上花费O(1)额外工作(因此将n节点Tree转换为SizedTree是O(n)工作,但是回报是检查SizedTree - 或任何子树的深度 - 是O(1)操作。

答案 1 :(得分:2)

您确实需要一些可以存储这些Int的数据。将Tree定义为

data Tree a = Node Tree a Tree | Leaf a

然后编写一个函数

annDepth :: Tree a -> Tree (Int, a)

您的原始TreeTree ()并且使用模式同义词可以恢复很好的构造函数。

如果由于某种原因想保留原始树,可以定义视图:

{-# LANGUAGE GADTs, DataKinds #-}

data Shape = SNode Shape Shape | SLeaf

data Tree a sh where
    Leaf :: a -> Tree a SLeaf
    Node :: Tree a lsh -> a -> Tree a rsh -> Tree a (SNode lsh rsh)

通过这种方式,您可以保证带注释的树具有与未注释的形状相同的形状。但如果没有适当的依赖类型,这种方法并不好。

另外,请查看问题Boilerplate-free annotation of ASTs in Haskell?

答案 2 :(得分:0)

标准解决方案是@DanielWagner建议的,只是扩展数据结构。这可能有点不方便,但可以解决:用于创建实例和使用records for pattern matching的智能构造函数。

也许Data types a la carte可以提供帮助,尽管我自己并没有使用过这种方法。基于此的库compdata

完全不同的方法是有效地记住您需要的值。我试图解决similar problem,其中一个解决方案由库stable-memo提供。请注意,这不是一种纯粹的功能方法,因为库内部基于对象标识,但接口是纯粹的,并且完美地用于此目的。