【问题标题】:Attempting to construct trees in Haskell尝试在 Haskell 中构建树
【发布时间】:2018-09-22 18:20:44
【问题描述】:

我正在尝试使用展开功能来构建树。

Tree t = Leaf | Node (Tree t) t (Tree t)

unfoldT :: (b -> Maybe (b, a, b)) -> b -> Tree a
unfoldT f b =
    case f b of
      Nothing -> Leaf
      Just (lt, x, rt) -> Node (unfoldT f lt) x (unfoldT f rt)

构建函数需要创建一个高度等于提供的数字的树,并按顺序编号。基本情况是 build 0 = Leaf,下一个是 build 1 = Node (Leaf 0 Leaf)。

build :: Integer -> Tree Integer

我尝试解决它:

build n = unfoldT (\x -> Just x) [0..2^n-2]

我不完全确定如何在这里构建树。 如果有人能指出我正确的方向,我会很高兴。

编辑 1:

如果我要使用 2 元组,我会组合什么?我需要能够以某种方式引用当前节点、它的左子树和它的右子树吗?

【问题讨论】:

  • 你为什么在最后写[0..n^n-2]
  • 提示:使用二元组作为b
  • 愚蠢的我。我改正了
  • Node (Leaf 0 Leaf) 是无效的表达式。它应该是(Node Leaf 0 Leaf) 或只是Node Leaf 0 Leaf

标签: haskell functional-programming binary-tree


【解决方案1】:

如果我要使用 2 元组,我会组合什么?

我建议传递剩余的深度以及从左侧的偏移量:

build = unfoldT level . (0,)
  where
    level (_, 0) = Nothing
    level (o, n) = let mid = 2^(n-1)
                   in ((o, n-1), o+mid-1, (o+mid, n-1))

【讨论】:

    【解决方案2】:

    如果我要使用 2 元组,我会组合什么?

    这是函数式编程中状态传递范式背后的关键问题,也用 State Monad 来表达。我们不会在这里处理后者,但可能会使用前者。

    但在此之前,我们真的需要生成一个列表中的所有数字,然后处理该列表吗?难道我们不知道提前我们将使用哪些数字?

    我们当然会这样做,因为我们正在建造的树是完全平衡的并且是完全填充的。

    如果我们有这样的功能

    -- build2 (depth, startNum)
    build2 :: (Int, Int) -> Tree Int
    

    我们可以使用它完全相同来构造例如的两半 build [0..14] 树:

    build [0..14] == build2 (4,0) == Node (build2 (3,0)) 7 (build2 (3,8))
    

    对吗?

    但如果我们不想直接计算所有涉及的数字,我们可以安排前面提到的状态传递,扭曲到build2 的界面:

    --     depth, startNum    tree, nextNum
    build3 :: (Int, Int) -> (Tree Int, Int)
    

    并像使用它

    build :: Int -> Tree Int                 -- correct!
    build depth = build3 (depth, 0)          -- intentionally incorrect
    
    build3 :: (Int, Int) -> (Tree Int, Int)  -- correct!
    build3 (depth, start) = Node lt n rt     -- intentionally incorrect
      where
           (lt, n) = build3 (depth-1, start)   -- n is returned
           (rt, m) = build3 (depth-1, n+1)     --  and used, next
    

    您将需要调整上述内容以使所有部分组合在一起(按照类型!),当然要实现缺少的部分并处理角落/基本案例。

    下一步就是将其制定为展开。

    【讨论】:

    • 我喜欢将nextNum 作为状态传递的想法(它避免了我的答案的重复计算),但我认为这种方法不适用于unfold。跨度>
    • 我还没有考虑清楚......我也有这个怀疑,但我不确定。无论如何,以这种方式写出来有助于澄清事情,正如 chi 在 cmets 中所建议的那样。 :)
    • 或者我们可以生成Tree (a, next_num_or_something) 作为另一个中间步骤...
    • 你总是可以手动创建一个临时树,然后将unfold 放入真正的树中,但那是(ab)使用unfold 作为fmap,我们可以放弃它,构建首先是正确的结果。
    • 是的,看起来不可能使用问题中定义的unfoldT。函数s -> Maybe (s, a, s) 必须同时产生a 两个s,所以它不适合。有趣的是,这个例子真正展示了递归和核心递归之间的区别,在上。
    猜你喜欢
    • 1970-01-01
    • 2014-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多