我有一个Rose Tree结构,我想为它写一个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
。
2条答案
按热度按时间k3bvogb11#
Traversable
必须与Foldable
一致。特别地,如果Monoid m
,则Applicative (Const m)
,导致一致性法则foldMap f = getConst . traverse (Const . f)
。因此,Breadth
和Depth
* 不可能 * 共享Traversable
。Traversable Breadth
存在与其Foldable
一致的不同实现,或者根本就没有。我可以编造一个我认为确实同意的实现,但我还没有验证其他的定律。这是相当棘手的,而且到处都是非整体性,但它应该工作得很好。
z31licg02#
这里是HTNW解决方案的一个变体,使用
Compose
代替递归调用时的扁平化结构,这意味着我们不需要重建结构,但可能也会更慢,因为它需要在每个递归步骤遍历一个深层结构。liftA2
与ZipList
一起被用于将zipWith
推广到任意多个Compose
d嵌套列表.ScopedTypeVariables
被用于为多态递归函数go
给予显式类型签名.