我正在尝试编写一个函数,该函数将生成一个列表,其中第一个元素被指定为该函数的参数,此后的每个元素与上一个元素的差异最多为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
... ]
运算符是否有严格的版本?
(编辑:我认为举一个我想要的列表类型的例子可能有用,而不是仅仅描述一个。例如>>=
)
答案 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
中实现。