Haskell 构建树中所有分支的列表
我需要让函数返回树中所有可能的分支 使用此表格:Haskell 构建树中所有分支的列表,haskell,tree,Haskell,Tree,我需要让函数返回树中所有可能的分支 使用此表格: data Tree a = EmptyT | NodeT a ( Tree a ) ( Tree a ) deriving (Show) everyBranch :: Tree a -> [[a]] 我不知道该怎么做。。。除息的 我还是哈斯克尔的新手 假设我有: 1 / \ 2 3 /\ / \ 4 5 7 8 我想得到:[[
data Tree a = EmptyT | NodeT a ( Tree a ) ( Tree a ) deriving (Show)
everyBranch :: Tree a -> [[a]]
我不知道该怎么做。。。除息的
我还是哈斯克尔的新手
假设我有:
1
/ \
2 3
/\ / \
4 5 7 8
我想得到:
[[1,2,4],[1,2,5],[1,3,8],[1,3,7]
我们将使用递归方法。让我们从一个粗略的骨架开始:
everyBranch :: Tree a -> [[a]]
everyBranch EmptyT = _something
everyBranch (NodeT v (Tree l) (Tree r)) = _somethingElse
现在我们来填补这些漏洞。(这种语法称为“类型化孔”:如果您通过GHC运行上述程序,它将给您一条错误消息,其中包含应在孔中的值的类型。)现在,我不确定第一种情况:根据您的需要,它可能是[]
(无分支)或[[]]
(一个分支没有元素),所以我们稍后再讨论这个问题。对于第二种情况,我们需要一种方法来构造给定v
值和l
eft和r
ights子树的分支列表。我们怎么做?我们将递归地查找l
eft树中的每个分支,以及r
ight树中的每个分支,然后我们将v
前置到这两个树:
everyBranch :: Tree a -> [[a]]
everyBranch EmptyT = _something
everyBranch (NodeT v l r) = map (v:) $ everyBranch l ++ everyBranch r
现在,让我们回到EmptyT
。考虑一个非常简单的树:<代码> NoDET 1 EMPTYT EMPTYT。在这种情况下,everyBranch
应该返回[[1]]
。让我们在这棵树上手动调用everyBranch:
(我使用└→
表示“递归计算子表达式”,而=>
表示“表达式计算为”)
所以在这里,我们希望map(1:)$\u something++\u something
等于[[1]]
。什么是什么东西?事实证明,如果某个东西是[]
,那么map(1:)$[]+[]
就是[]
,这不是我们想要的。另一方面,如果某个东西是[[]]
,那么map(1:)$[[]]+[[]]]
就是[[1],[1]]
,这也不是我们想要的。看起来我们需要一个稍微不同的方法。我们将要做的是,我们将为这类树添加另一个案例:
everyBranch :: Tree a -> [[a]]
everyBranch EmptyT = _something
everyBranch (NodeT v EmptyT EmptyT) = [[v]]
everyBranch (NodeT v l r) = map (v:) $ everyBranch l ++ everyBranch r
现在,如果我们稍微测试一下(尽管对
\u something
使用一些随机值来阻止它给我们带来错误),我们发现它适用于所有二叉树。如前所述,我们仍然需要计算出\u某物的值。此值仅在两种情况下起作用:空树(在这种情况下,它将与EmptyT
)和只有一个子树的树(在这种情况下,l
或r
将与EmptyT
匹配)。我将把它作为一个练习留给您来确定放在那里的值,它将如何影响结果,以及它为什么会以这种方式影响结果。我们可以派生并使用Foldable
,折叠成一个特殊的monoid来完成这项工作:
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
(提示节点)
是我们真正想要的,但是
是二进制的,我们不知道(如果我们知道的话,也不应该依赖它)根据foldMap
的派生定义将部分组合成整体的实际顺序
foldMap T EmptyT == N []
foldMap T (NodeT a lt rt) == T a <> foldMap T lt <> foldMap T rt
-- but in what order?
foldMap T EmptyT==N[]
折叠贴图T(节点a lt rt)=T a折叠贴图T lt折叠贴图T rt
--但顺序是什么?
因此,我们通过延迟实际组合直到三个部分都可用,来“伪造”
或者我们可以完全放弃派生路线,使用上述定律作为自定义foldMap
的定义,使用三元组合,最终得到。。。与另一个答案中的递归代码相当——总体上要短得多,没有需要隐藏在模块墙后面的一次性辅助类型的实用性缺陷,并且不言而喻是非局部的,这与我们最终得到的不同
所以也许不是很好。无论如何,我会把它贴出来,作为对位
> 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]]
foldMap T EmptyT == N []
foldMap T (NodeT a lt rt) == T a <> foldMap T lt <> foldMap T rt
-- but in what order?