我们可以派生并使用Foldable,折叠成一个临时的幺半群来完成这项工作:
data Tree a = EmptyT
| NodeT a ( Tree a ) ( Tree a )
deriving (Show, Functor, Foldable)
data T a = T a -- tip
| N [[a]] -- node
| TN (a,[[a]]) -- tip <> node
| NN ([[a]],[[a]]) -- node <> node
deriving Show
instance Monoid (T a) where
mempty = N [] -- (tip <> node <> node) is what we actually want
mappend (T a) (N as) = TN (a,as) -- tip <> node
mappend (N as) (N bs) = NN (as,bs) -- node <> node
mappend (T a) (NN ([],[])) = N ([[a]]) -- tip <> (node <> node)
mappend (T a) (NN (as,bs)) = N (map (a:) as ++ map (a:) bs)
mappend (TN (a,[])) (N []) = N ([[a]]) -- (tip <> node) <> node
mappend (TN (a,as)) (N bs) = N (map (a:) as ++ map (a:) bs)
allPaths :: Tree a -> [[a]]
allPaths (foldMap T -> N ps) = ps
allPaths 函数定义使用ViewPatterns。测试,
> allPaths $ NodeT 1 (NodeT 2 (NodeT 3 EmptyT EmptyT) EmptyT)
(NodeT 5 EmptyT EmptyT)
[[1,2,3],[1,5]]
> allPaths $ NodeT 1 (NodeT 2 (NodeT 3 EmptyT EmptyT) (NodeT 4 EmptyT EmptyT))
(NodeT 5 EmptyT EmptyT)
[[1,2,3],[1,2,4],[1,5]]
(tip <> node <> node) 是我们真正想要的,但 <> 是二进制的,我们不知道(如果我们知道也不应该依赖它)将部分组合成整体的实际顺序通过foldMap的派生定义,
foldMap T EmptyT == N []
foldMap T (NodeT a lt rt) == T a <> foldMap T lt <> foldMap T rt
-- but in what order?
所以我们“伪造”,它通过延迟实际组合直到所有三个部分都可用。
或者我们可以完全放弃派生路线,使用上述定律作为自定义 foldMap 的定义与三元组合,并最终得到......相当于另一个答案中的递归代码 - 很多整体更短,没有需要隐藏在模块墙后面的一次性辅助类型的实用性,并且不言而喻是非部分的,不像我们在这里结束的那样。
所以也许它不是那么好。无论如何,我都会发布它,作为对立面。