【问题标题】:Building a list of all branches in a tree构建树中所有分支的列表
【发布时间】:2019-04-13 05:02:34
【问题描述】:

我需要让函数从树中返回所有可能的分支 使用这种形式:

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]]

【问题讨论】:

    标签: haskell tree


    【解决方案1】:

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

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

    现在我们将填补这些漏洞。 (这种语法被称为“类型孔”:如果您通过 GHC 运行上述程序,它会给您一条错误消息,其中包含应该在孔中的值的类型。)现在,我不确定第一种情况:根据您的需要,它可能是[](没有分支)或[[]](一个没有元素的分支),所以我们稍后再讨论。对于第二种情况,我们需要一种方法来构造一个给定value 和left 和right 子树的分支列表。我们如何做到这一点?我们将递归查找left 树中的每个分支,以及right 树中的每个分支,然后我们将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)。我将把它留作练习,让您确定应该放什么值,它将如何影响结果,以及为什么会以这种方式影响结果。

    【讨论】:

      【解决方案2】:

      我们可以派生并使用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 &lt;&gt; node &lt;&gt; node) 是我们真正想要的,但 &lt;&gt; 是二进制的,我们不知道(如果我们知道也不应该依赖它)将部分组合成整体的实际顺序通过foldMap的派生定义,

      foldMap T EmptyT          ==  N []
      foldMap T (NodeT a lt rt) ==  T a <> foldMap T lt <> foldMap T rt
                                    -- but in what order? 
      

      所以我们“伪造”,它通过延迟实际组合直到所有三个部分都可用。

      或者我们可以完全放弃派生路线,使用上述定律作为自定义 foldMap 的定义与三元组合,并最终得到......相当于另一个答案中的递归代码 - 很多整体更短,没有需要隐藏在模块墙后面的一次性辅助类型的实用性,并且不言而喻是非部分的,不像我们在这里结束的那样。

      所以也许它不是那么好。无论如何,我都会发布它,作为对立面。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-12-28
        • 1970-01-01
        • 2015-01-01
        • 1970-01-01
        • 2013-02-27
        • 1970-01-01
        • 2018-01-23
        相关资源
        最近更新 更多