代码之家  ›  专栏  ›  技术社区  ›  Zac

每个元素与前一个元素最多相差1的随机列表

  •  1
  • Zac  · 技术社区  · 6 年前

    import Data.List
    import System.Random
    
    step :: Int -> IO Int
    step n = (+n) <$> randomRIO (-1, 1)
    
    steps :: Int -> Int -> IO [Int]
    steps n = sequence . take n . iterate' (>>= step) . return
    

    )我也试过用非严格的 iterate 函数,得到了相同的结果)。

    这个 step 函数获取一个整数,然后随机向其中添加-1、0或1。这个 steps 函数接受要执行的迭代次数和起始整数,并应用

    问题是 步骤 给我的感觉是 [0,1,-1,0,1,1,1,3] ,这是错误的。看起来每个元素每次都是从头开始生成的,而我希望每个元素都依赖于前一个元素。这就是我决定使用 iterate' 而不是 迭代 ,这表示在继续之前,它会将每个迭代减少到WHNF,但即使这样,它仍然不起作用。

    然后我意识到问题可能来自这样一个事实,即它实际上会生成一个如下所示的列表:

    [ n,
      n >>= step,
      n >>= step >>= step
      ... ]
    

    >>= 接线员?

    [0, 1, 2, 1, 2, 1, 0, -1] ,例如)

    1 回复  |  直到 6 年前
        1
  •  8
  •   Zeta    6 年前

    >>= iterate 计算

    [ return x , return x >>= step, return x >>= step >>= step, ... ]
    

    你需要一个 迭代 :

    -- This function does not work, but shows the principle we would
    -- want from such a function.
    iterateM :: Monad m => (a -> m a) -> a -> m [a]
    iterateM f x = do
         y  <- f x
         ys <- iterateM f y -- << this never terminates
         return (y:ys)
    

    但是,该变体不存在*,因为它不会终止,原因与 forM [1..] return 不终止。但是,如果将算法更改为 产生差异 replicateM 总和 这些差异 scanl

    import Control.Monad (replicateM)
    import System.Random (randomRIO)
    
    step :: IO Int
    step = randomRIO (-1, 1)
    
    steps :: Int -> Int -> IO [Int]
    steps n x = scanl (+) x <$> replicateM n step
    

    在这种情况下,我们有有限的 step s在 IO 扫描

    *流媒体库中有一些变体 可以决定是否可以运行计算。 iterateM 可以在那里实现,例如 ConduitM .