如何使用Data.Array
生成随机数组?
我有功能,它给了我一个随机数:
randomNumber :: (Random r) => r -> r -> IO r
randomNumber a b = getStdRandom (randomR (a,b))
然后我尝试使用Data.Array
中的函数生成列表
assocs $ array (1,100) [(i,i) | i <- (randomNumber 1 10)]
我知道,randomNumber
的类型是IO
,是否有任何方法可以转换IO Int -> Int
?或者我需要使用其他方法来获取随机列表?我应该在bind
块中使用do
运算符执行这些功能吗?
答案 0 :(得分:11)
你应该使用函数从生成器生成纯粹的随机列表,然后使用getStdRandom
:
randomList :: Int -> Int -> IO [Int]
randomList a b = getStdGen >>= return . randomRs (a,b)
您需要的功能是randomRs。然后,您使用stdGen
将生成器设置为getStdGen
,然后就可以使用生成器。
函数randomList
首先使用getStdGen
获取标准生成器,然后将其传递给randomRs
。请注意,randomList
可以在不隐藏生成器参数的情况下重写:
randomList a b = getStdGen >>= \gen -> return (randomRs (a,b) gen)
答案 1 :(得分:5)
只要@ mariop的回答讲述列表而不是数组,我就会继续,并尝试更多地解释Haskell随机性的本质。
(如果您对理论不感兴趣,请跳至(tl; dr)部分)
首先,让我们为我们推测的功能选择一个签名。我认为你需要一个普通的数组(如在C或Java中),用连续的自然数字索引(如果我猜错了,请更正)。
如您所知,所有Haskell函数都是纯粹且确定的,因此每个函数必须始终为相同的参数返回相同的结果。当然,这不是随机的情况。解决方案是使用伪随机值,其中我们有生成器。生成器本身是一个复杂的函数,具有一个名为 seed 的内部隐藏状态,并且可以生成一个值和一个带有新种子的新生成器(然后可以生成一个新的(值,生成器)对和等等)。建立一个好的生成器,以便无法从之前的值(当我们不知道种子时)预测下一个值,因此它们对用户来说是随机的。
事实上,大多数语言中的所有主要随机实现都是 伪随机因为&#34; true&#34;随机(从中获取其值) &#34;自然&#34;的来源随机性,称为 entropy ,如CPU温度) 计算上很昂贵。
Haskell中的所有所谓随机函数都以某种方式处理生成器。如果您查看Random
类型类中的方法,它们将分为两组:
明确获取随机生成器的那些:randomR
,random
等等。您可以使用mkStdRandom构建一个使用种子初始化的显式生成器(甚至可以创建自己的生成器)。
在IO monad中工作的那些:randomIO
,randomRIO
。他们实际上从环境中获取发电机&#34;携带&#34;在IO monad中(使用getStdRandom
),并从第一组开始运行。
因此,我们可以用任何一种方式组织我们的功能:
--Arguments are generator, array size, min and max bound
generateArray :: (RangomGen g, Random r) => g -> Int -> r -> r -> Array Int r
或
--Arguments are array size, min and max bound
generateArray :: Random r => Int -> r -> r -> IO (Array Int r)
因为Haskell是懒惰的,所以不需要制作一组固定的随机值 - 我们可以创建一个无限,并根据需要获取尽可能多的值。随机有界值的无限列表由randomRs
函数生成。
(TL; DR)
如果数组是连续的,更简单的方法是从普通值列表而不是assocs(键,值)列表构建它:
generateArray gen size min max =
listArray (0, size - 1) $ randomRs (min, max) gen
或
generateArray size min max =
getStdGen >>= return . listArray (0, size - 1) . randomRs (min, max)