建立树中所有分支的列表

时间:2019-04-13 05:02:34

标签: haskell tree

我需要使函数从树中返回所有可能的分支 格式如下:

data Tree a = EmptyT | NodeT a ( Tree a ) ( Tree a ) deriving (Show)

everyBranch :: Tree a -> [[a]]

我不确定该如何处理... xD 我仍然是Haskell的新手。

让我说:

            1
           / \
          2   3
         /\  / \
        4  5 7  8

我想获得:[[1,2,4], [1,2,5], [1,3,8], [1,3,7]]

2 个答案:

答案 0 :(得分:7)

我们将使用递归方法。让我们从一个粗略的骨架开始:

everyBranch :: Tree a -> [[a]]
everyBranch EmptyT = _something
everyBranch (NodeT v (Tree l) (Tree r)) = _somethingElse

现在,我们将填补漏洞。 (此语法称为“类型化的孔”:如果您通过GHC运行上述程序,则会向您显示一条错误消息,其中应包含该孔中的值的类型。)现在,我不确定第一种情况:根据您的需要,它可能是[](无分支)或[[]](一个无元素的分支),所以我们稍后再讲。对于第二种情况,我们需要一种方法来构造给定v alue和l eft和r ight子树的分支列表。我们该怎么做?我们将以递归方式找到l分支树中的每个分支,以及r分支树中的每个分支,然后将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

(我用└→表示“递归评估子表达式”,而=>则表示“表达式求值”)

everyBranch (NodeT 1 EmptyT EmptyT)
=> map (1:) $ everyBranch EmptyT ++ everyBranch EmptyT
   └→ everyBranch EmptyT
      => _something
=> map (1:) $ _something ++ _something

所以在这里,我们希望map (1:) $ _something ++ _something等于[[1]]。什么是_something?好吧,事实证明,如果_something[],那么map (1:) $ [] ++ [][],这不是我们想要的。另一方面,如果_something[[]],则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

现在,如果我们对此进行一点测试(尽管使用_something的一些随机值来阻止它给我们带来错误),我们会发现它适用于所有二叉树。如前所述,我们仍然需要弄清楚_something的值。该值仅在以下两种情况下才有意义:空树(在这种情况下它将与EmptyT微妙地匹配)和仅具有一个子树的树(在这种情况下lr将匹配EmptyT)。我将把它留作练习,让您确定要放置的值,它将如何影响结果以及为什么会以这种方式影响结果。

答案 1 :(得分:0)

我们可以派生并使用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

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的定义,最后得到...等于其他答案中的递归代码- -总的来说要短得多,而无需在模块壁后面隐藏一次性的辅助类型的功利性,而且显然不完全是非局部的,这与我们最终得到的结果不同。

所以也许不是很好。无论如何,我都会将其发布,作为对策。