【问题标题】:Why does the function behave this way? [closed]为什么函数会这样? [关闭]
【发布时间】:2021-12-22 13:49:34
【问题描述】:

我编写了一个函数,它使用任何类型的遍历将二叉树收集到属于Monoid 类型类的任何类型。

树的类型定义如下:

data BinTree a = EmptyBT |
                 NodeBT a (BinTree a) (BinTree a)
                 deriving (Eq, Ord, Show)

不同类型遍历的枚举类型:

data TravOrder = Inorder | Preorder | Postorder

我第一次尝试编写函数:

collectBT :: Monoid m => TravOrder -> (a -> m) -> BinTree a -> m
collectBT Inorder = \f tree -> 
    case tree of
        EmptyBT -> mempty
        NodeBT val lt rt -> recursion lt <> f val <> recursion rt
            where recursion = collectBT Inorder f

collectBT Preorder = \f tree ->
    case tree of
        EmptyBT -> mempty
        NodeBT val lt rt -> f val <> recursion lt <> recursion rt
            where recursion = collectBT Preorder f

collectBT Postorder = \f tree ->
    case tree of
        EmptyBT -> mempty
        NodeBT val lt rt -> recursion lt <> recursion rt <> f val
            where recursion = collectBT Postorder f

此版本运行正常。然后我删除了重复的代码:

combineInOrder :: Monoid m => TravOrder -> m -> m -> m -> m
combineInOrder Inorder = \val lt rt -> lt <> val <> rt
combineInOrder Preorder = \val lt rt -> val <> lt <> rt
combineInOrder Postorder = \val lt rt -> lt <> rt <> val

collectBT' :: Monoid m => TravOrder -> (a -> m) -> BinTree a -> m
collectBT' ord f EmptyBT = mempty
collectBT' ord f (NodeBT val rt lt) =
    let recursion = collectBT ord f 
    in combineInOrder ord (f val) (recursion lt) (recursion rt)

现在函数返回不正确的结果。我认为它的行为方式应该相同,因为 monoids 以相同的顺序组合。我错过了什么?

表达式生成简单树以防有人想运行它:

(NodeBT 1 (NodeBT 2 EmptyBT EmptyBT) (NodeBT 3 EmptyBT EmptyBT))

  1
 / \
2   3

【问题讨论】:

  • 你在recursion中调用collectBT(不是collectBT')?
  • 你写NodeBT val rt lt作为你的模式。你的意思可能是NodeBT val lt rt
  • @DanielWagner 就是这样。谢谢!我在这上面浪费了太多时间。
  • “不正确的结果”。 -- 布尔失明再次来袭!

标签: haskell recursion


【解决方案1】:

您可能会做的一个简化是避免单独的TravOrder 类型:它实际上只是“真实”遍历顺序前面的一个间接级别,它是a -&gt; a -&gt; a -&gt; a 类型的函数,它结合了左侧,对了,val 结果是整个节点的结果。

data BinTree a = EmptyBT |
                 NodeBT a (BinTree a) (BinTree a)
                 deriving (Eq, Ord, Show)

type TraversalOrder a = a -> a -> a -> a
preOrder, inOrder, postOrder :: Monoid m => TraversalOrder m
preOrder val lt rt = val <> lt <> rt
inOrder val lt rt = lt <> val <> rt
postOrder val lt rt = lt <> rt <> val

collectBT :: Monoid m => TraversalOrder m -> (a -> m) -> BinTree a -> m
collectBT trav f EmptyBT = mempty
collectBT trav f (NodeBT val lt rt) = trav (f val) (collectBT trav f lt) (collectBT trav f rt)

您可能会注意到collectBT trav v 的重复有点烦人。您可以根据需要将其提取到 where 子句中(练习留给读者),或者您可以找点乐子并根据您的树类型的变质来编写此遍历:

cataBT :: b -> (a -> b -> b -> b) -> BinTree a -> b
cataBT fin combine t = case t of
  EmptyBT -> fin
  NodeBT val lt rt -> combine val (go lt) (go rt)
    where go = cataBT fin combine

collectBT :: Monoid m => TraversalOrder m -> (a -> m) -> BinTree a -> m
collectBT trav f = cataBT mempty (trav . f)

这个cataBT 以任何方式递归地折叠你的树; collectBT 是一个简单的特殊情况,它为EmptyBT 规定了mempty,并在委托给trav 进行组合之前调用f

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-02
    • 2013-07-28
    • 1970-01-01
    • 2015-02-10
    • 2015-09-05
    • 1970-01-01
    • 2018-10-15
    相关资源
    最近更新 更多