haskell - 每个元素与前一个元素最多相差 1 的随机列表
问题描述
我正在尝试编写一个将生成一个列表的函数,其中第一个元素被指定为函数的参数,之后的每个元素与前一个元素的差异最多为 1。这是我尝试过的:
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
函数需要执行一定数量的迭代和一个起始整数,并应用step
正确的次数。
问题是这steps
给了我类似的东西[0,1,-1,0,1,1,1,3]
,这是错误的。看起来每个元素每次都是从头开始生成的,而我希望每个元素都依赖于前一个元素。这就是我决定使用iterate'
而不是的原因iterate
,它表示它在继续之前将每次迭代减少到 WHNF,但即使它仍然不起作用。
然后我意识到问题可能源于它实际上会生成一个看起来像这样的列表:
[ n,
n >>= step,
n >>= step >>= step
... ]
然后似乎很清楚为什么会发生这种情况。所以我的问题是,我可以防止这种情况吗?我可以强制 Haskell 评估每个元素吗?是否有严格版本的>>=
运算符?
(编辑:我认为举一个我正在寻找的列表的例子可能很有用,而不是仅仅描述一个。[0, 1, 2, 1, 2, 1, 0, -1]
,例如)
解决方案
您不需要严格版本的>>=
. 您需要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
不会终止的相同。但是,如果将算法更改为首先生成差异,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
并使用通常scanl
的方式来生成您想要的列表。
* 流库中有一些变体,消费者可以决定是否可以运行计算。iterateM
可以在那里实现,例如在ConduitM
.
推荐阅读
- python - 如何使用flask在html页面上显示python控制台输出
- c# - MS Teams:通过链接导航到聊天消息
- c# - 在cshtml视图中的两个地方使用时如何修复mentionsInput插件不起作用
- matlab - 数据集的准确性
- javascript - 将带有#长度的字符串等分
- python - Numpy 数组中图像的图像数据生成器
- javascript - 为什么 get 属性的工作方式与 Vue 中的函数不同,看起来我必须使用函数而不是 getter 一些未知的幕后原因
- amazon-web-services - 无法查看 Cloudformation 模板
- python - 数据抓取:除非我通过主网站加载,否则网页不存在
- html5-audio - 为什么振荡器的网络音频输出没有按预期工作?