广度优先和深度优先的树的遍历是否不同?

时间:2019-06-09 23:57:05

标签: haskell tree traversable

我有一个玫瑰树结构,我想为其编写一个Traversable实例。因此,我从以下内容开始:

data Tree a = Tree a [Tree a] deriving (Show)

instance Functor Tree where
  fmap f (Tree x subs) = Tree (f x) (fmap (fmap f) subs)

我做了深度优先的变体:

newtype Depth a = Depth (Tree a) deriving (Show)

depth :: Tree a -> [a]
depth (Tree x subs) = x : concatMap depth subs

instance Functor Depth where
  fmap f (Depth t) = Depth $ fmap f t

instance Foldable Depth where
  foldMap f (Depth t) = mconcat $ f <$> depth t

instance Traversable Depth where
  traverse f (Depth t) = Depth <$> go t
    where go (Tree x subs) = Tree <$> f x <*> traverse go subs

然后我尝试了广度优先的变体:

newtype Breadth a = Breadth (Tree a) deriving (Show)

breadth :: Tree a -> [a]
breadth tree = go [tree]
  where
    go [] = []
    go (Tree x subs:q) = x : go (q <> subs)

instance Functor Breadth where
  fmap f (Breadth t) = Breadth $ fmap f t

instance Foldable Breadth where
  foldMap f (Breadth t) = mconcat $ f <$> breadth t

instance Traversable Breadth where
  traverse f (Breadth t) = ???

我意识到Traversable的广度和深度优先变体应该是相同的。是这样吗我不相信我实际上已经在任何地方阅读过了,但是遍历与元素的顺序无关吗?

如果是这样,这会有些奇怪,因为然后可以直接为Traversable实现Tree,这意味着Foldable需要为Tree实现,但是在那里Foldable的实现方式显然有多种。

1 个答案:

答案 0 :(得分:5)

Traversable必须同意Foldable。具体来说,如果为Monoid m,则为Applicative (Const m),导致一致性定律为foldMap f = getConst . traverse (Const . f)。因此,BreadthDepth共享Traversable不可能Traversable Breadth有一个不同的实现与其Foldable一致,或者根本没有实现。我可以制作一个我认为确实可以实现的实现,但是我还没有验证其他法律。

instance Traversable Breadth where
  traverse f (Breadth t) = Breadth <$> head <$> go [t]
    where
      go [] = pure []
      go ts = zipWith Tree <$> traverse f rs
                           <*> (fmap (rebuild css) $ go $ concat css)
        where
          (rs, css) = unzip $ map (\(Tree r cs) -> (r, cs)) ts
          -- rebuild s d = evalState (traverse (state splitAt') d) s
          -- I think, but let's keep the dependencies down, shall we?
          rebuild [] [] = []
          rebuild (struct : structs) destruct
            = let (cs, destruct') = splitAt' struct destruct
              in  cs : rebuild structs destruct'
          -- ignoring the as in a [a] makes it look like a number
          splitAt' [] xs = ([], xs)
          splitAt' (_ : n) (x : xs)
            = let (pre, suf) = splitAt' n xs
              in  (x : pre, suf)

这很毛茸茸,到处都是非总计,但应该可以。