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

时间:2019-03-16 13:18:02

标签: haskell random monads lazy-evaluation

我正在尝试编写一个函数,该函数将生成一个列表,其中第一个元素被指定为该函数的参数,此后的每个元素与上一个元素的差异最多为1。这是我尝试过的:

Re_Enable

(我也尝试使用非严格的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函数使用一个整数,并随机向其添加-1、0或1。 step函数需要执行一定的迭代次数和一个起始整数,并应用正确的次数steps

问题是step给了我steps之类的东西,这是错误的。看起来每个元素都是从头开始生成的,而我希望每个元素都依赖于前一个元素。这就是我决定使用[0,1,-1,0,1,1,1,3]而不是iterate'的原因,它表示在继续之前将每次迭代减少到WHNF,但仍然无法正常工作。

然后我意识到问题可能出在以下事实上:它将实际上生成类似于以下内容的列表:

iterate

然后清楚为什么会发生。所以我的问题是,我可以防止这种情况发生吗?我可以强迫Haskell对每个要素进行评估吗? [ n, n >>= step, n >>= step >>= step ... ] 运算符是否有严格的版本?

(编辑:我认为举一个我想要的列表类型的例子可能有用,而不是仅仅描述一个。例如>>=

1 个答案:

答案 0 :(得分:8)

您不需要严格的>>=版本。您需要iterate的单子变体。毕竟,您已经确定了问题所在,正在建立无限数量的计算

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

您将需要iterate的单字变体:

-- 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不终止相同。但是,如果将算法更改为 first 首先使用replicateM生成差异,然后使用scanl sum 生成差异,则可以解决此问题:< / p>

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中的IO数量有限,并使用通常的scanl来生成所需的列表。

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