【问题标题】:Haskell IO: remove a random element from a treeHaskell IO:从树中删除随机元素
【发布时间】:2014-03-06 02:05:33
【问题描述】:

考虑以下类型来表示树:

data Tree a = Empty
            | Leaf a
            | Fork (Tree a) (Tree a)

我需要帮助定义函数removeRandom' :: Tree a -> IO (Tree a),它接收至少有一个叶子的树并返回从树中删除随机叶子的结果(用 Empty 替换它)。练习有一个建议:使用函数randomRIO :: Random a => (a,a) -> IO a生成要移除的元素的顺序

编辑:尝试用户 Thomas 的方法 2

removeRandom' :: Tree a -> IO (Tree a)
removeRandom' t = let lengthTree = numbelems t
                  in do x <- randomRIO (0,lengthTree -1)
                        return (remove x t)

numbelems :: Tree a -> Int
numbelems Empty = 0
numbelems Leaf x = 1
numbelems Fork l r = (numbelems l) + (numbelems r)

remove :: Int -> Tree a -> Tree a
remove _ (Leaf x) = Empty
remove n (Fork l r) = let lengthLeft = numbelems l
                      in if (n>lengthLeft) then Fork l (remove (n-lengthLeft r)
                         else Fork (remove n l) r

【问题讨论】:

    标签: list haskell build io tree


    【解决方案1】:

    有两种方法可以解决这个问题

    1. 转换为列表,删除元素,然后转换回树。

      • 优点:实现简单,你已经有了toList,你只需要fromList,你可以简单地实现你的解决方案

        removeAt :: Int -> [a] -> [a]
        removeAt n as = a ++ tail s where (a, s) = splitAt n
        
        removeRandom' tree = do
            element <- randomRIO (0, length tree)
            return $ fromList $ removeAt element $ toList tree
        
      • 缺点:此方法对问题陈述 removing a random leaf from the tree (replacing it with Empty) 不“正确”,并且可能会给您一棵没有 Empty 值的全新树。我仅将其作为一个选项提供,以尝试显示您的 toList 方法的结束位置。

    2. 下降到树中,直到你碰到要移除的元素,然后在返回的路上重建树

      • 优点:算法的核心是“纯”,如IO。在removeRandom' 中,您实际上只需要片刻的 IO。您可能会编写一个看起来有点像这样的解决方案(有趣的部分留空;)。

        removeAt :: Int -> Tree a -> Tree a
        removeAt n tree = walk 0 tree
          where
            walk i Empty = ...
            walk i (Fork l r) = ...
            walk i l@(Leaf _)
              | i == n    = ...
              | otherwise = ...
        
        removeRandom' tree = do
            element <- randomRIO (0, length tree)
            return $ removeAt element tree
        
      • 缺点:实现起来更复杂,您需要知道如何“向上”遍历一棵树,并在之后重建,并且您需要知道如何编写递归具有累加器的功能,以便您可以跟踪您在树中的位置。

    无论您决定采用哪种方式,您都需要编写一个函数length :: Tree a -&gt; Int,该函数计算叶子的数量以用作randomRIO 的输入(这是一个在给定范围内简单地产生随机值的操作) .

    【讨论】:

    • 感谢您的详细解释。我会尝试方法二
    • 好的。我在我的问题中添加了我对方法二的尝试。你能看一下吗?
    • 您能帮我构建 fromList 函数吗?我知道它与方法二无关,但我认为它可能在另一种情况下有用
    • 您对 numbelems 的实现是正确的,但您对 remove 的实现却不是。用 Empty 替换 Leaf 的条件是当您的累加器达到零时。因此,您必须计算到目前为止遇到的所有叶子,并将它们从累加器中删除。
    • 好的,我会试试你的方法。但是,我认为我的也有效。你能给我一个失败的树的例子吗?
    猜你喜欢
    • 2019-12-25
    • 2018-07-19
    • 2016-05-17
    • 2017-12-06
    • 1970-01-01
    • 1970-01-01
    • 2019-02-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多