Haskell如何生成无穷多的无限随机数列表用于模拟?

Haskell how to generate infinite number of lists of infinite random numbers for simulation?

在模拟问题中,我需要生成数千个行为彼此独立的代理对象。为此,我需要向这些代理中的每一个传递一个不同的随机数生成器。如何在 Haskell 中执行此操作? 在像 C 这样的语言中,我可以在需要时生成一个随机数,但在 Haskell 中,我只需要在 IO monad 中进行。代理在完全纯计算中的行为。

我现在是按以下方式做的:

import System.Random
main = do
   gen1 <- newStdGen
   let rands1 = randoms gen :: [Int]
   gen2 <- newStdGen
   let rands2 = randoms gen :: [Int]
   let sout = map (agentRun extraArgs) [rands1,rands2]
   writeFile "output" $ unlines sout

agentRun = <<some pure code that uses random numbers>>

下面给出了我可以解决这个问题的一种方法,但它的缺点是它只需要在 IO monad 中是 运行 并且我不能只将函数映射到列表上。有没有更好、更像 Haskell 的方法来做到这一点?如果是,请给出示例代码示例。

请注意,代理需要访问无限列表,因为事先不知道需要多少个随机数。

import System.Random
main = do
   dorun 10
dorun 0 = return ()
dorun n = do
   gen1 <- newStdGen
   let rands1 = randoms gen :: [Int]
   let sout = agentRun extraArgs rands1
   appendFile "output" sout
   dorun (n-1)

agentRun = <<some pure code that uses random numbers>>

推测您有一些包含代理参数的结构和一个函数

agentRun :: AgentParams -> [Int] -> Agent

您的问题是,如何为每个代理创建无限的 Int 列表。

假设您从一个 AgentParam 列表开始,并且您想要映射 agentRun 到它上面,如下所示:

agentsParams :: [AgentParams]
agents = map (\p -> agentRun p ????) agentParams

问题是,您看不到如何将不同的无限列表放入每个代理中。

为此,您可以使用库函数 unfoldrsplit:

unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
split :: (RandomGen g) => g -> (g,g)

unfoldr 继续应用其功能,直到 returns Nothing

split 使用一个随机生成器和 returns 两个新的。所以 Just . splitunfoldr 提供了一个很好的论据。

infiniteGenerators :: (RandomGen g) => g -> [g]
infiniteGenerators = unfoldr (Just . split)

您现在可以将这个生成器的无限列表映射到无限列表的无限列表中:

infiniteRandomLists :: (RandomGen g) => g -> [[Int]]
infininteRandomLists = map randoms . infiniteGenerators

然后您可以使用 zipWith 获取您的代理列表:

agents gen = zipWith agentRun (infiniteRandomLists gen)

然而,在编写代理代码时,您可能会发现使用生成器比使用无限的 Int 列表更容易。这是因为此类函数中存在一种常见模式,您可以在其中编写:

(gen2, gen3) = split gen1
result = foo gen2

这样您就不必将更新后的生成器传回 foo。如果您要从无限列表中获取数字,那么您将必须执行以下操作:

(randomList2, result) = foo randomList1

但更好的解决方案是使用某种随机状态 monad 来为您管理所有这些。