我想生成一个范围内的随机数,类型签名为Int -> Int。我读过其他几篇文章,但是都没有建议返回类型的方法Int。我System.IO.Unsafe在代码中使用过,但不建议这样做。这是我的代码:
import System.IO.Unsafe
-- random number generator
rng :: Int -> Int
rng upper = unsafePerformIO $ randomRIO (0,upper-1)
Run Code Online (Sandbox Code Playgroud)
有谁对在Haskell范围内如何生成随机Int有任何建议?
编辑:可能无法更改,IO Int -> Int所以我将代码转换为
-- random number generator
rng :: Int -> IO Int
rng upper = randomRIO (0,upper-1)
Run Code Online (Sandbox Code Playgroud)
之所以需要rng是因为我想获取列表范围长度内的随机数,以获得列表元素的索引。
list !! rng (length list)但我得到了Couldn't match expected type ‘Int’ with actual type ‘IO Int’预期的错误。
它不是重复的,因为1.我想要一个范围内的值,2.我的rng不返回相同的值。我是Haskell的新手,我不知道如何操纵Monad。任何帮助表示赞赏。
我本着https://xkcd.com/221/的精神,这是一个没有任何IO的“解决方案”:
rng :: Int -> Int
rng upper
| upper<=4 = upper
| otherwise = 4
Run Code Online (Sandbox Code Playgroud)
这样就为您提供了“符合RFC 1149.5的随机数”。除非超出范围,否则始终为四个。
这是什么问题?好吧,很明显,它总是提供相同的数字,所以也必须如此,因为所有Haskell函数都必须是函数,即引用透明。OTOH,一个随机数生成器应该在您每次调用它时给出不同的数字...因此,它不是一个函数,大多数其他编程语言都只是假装它是一个有副作用的函数–因为它们没有适当的方法来表达副作用。好吧,Haskell确实有表达这种情况的适当方法,这就是IO monad:您可以进行依赖于副作用的计算,但是很明显,如果运行这些计算,它们本身将具有副作用。
因此,签名Int -> IO Int对于功能确实有意义。(这是一个函数,但是结果是IO操作,只有执行该操作才会给您一个Int。)
什么是丑陋有关的是,IO Int可以从字面上做任何事情的IO-它可能,例如,推出一些导弹和退给你的伤亡人数。实际上,它可以轻松地修改主目录中的某些文件。而实际上您想要的只是很小的,无害的副作用,足以在下一次产生新的随机数。通常,随机数生成器实际上并不是真正的随机数,而是PRNG,PRNG保留一个恒定大小的状态变量,该变量在每次拉取值时都会以随机的方式进行更新。下次,此状态将有所不同,因此您将根据需要获得不同的值。此状态变量可以保存在IO-可变位置
import Data.IORef
type RandStV = Int
type RandSt = IORef RandStV
rng' :: RandSt -> Int -> IO Int
rng' rSt upper = do
x <- readIORef rSt
let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff -- https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/random_r.c;hb=glibc-2.26#l362
writeIORef rSt x'
return $ x `mod` upper
Run Code Online (Sandbox Code Playgroud)
...或者,您可以明确地将更新状态和结果一起传递
rng'' :: Int -> RandStV -> (RandStV, Int)
rng'' upper x =
let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff
in (x', x `mod` upper)
Run Code Online (Sandbox Code Playgroud)
...或者可以在专用状态monad中传递它,这是编写更新变量传递的另一种方式:
type RandStM = State RandStV
rng''' :: Int -> RandStM Int
rng''' upper = do
x <- get
let x' = ((x * 1103515245) + 12345) `mod` 0x7fffffff
put x'
return $ x `mod` upper
Run Code Online (Sandbox Code Playgroud)
有关此类随机monad的有用帮助程序,请参见random-fu软件包。
一种数学上的解释方式rng'''是说它是一个以上限为参数并返回数字分布的函数。分布总是相同的,但是它“包含”许多数字以及它们出现的可能性。实际上生成一个整数意味着您正在从分布中采样。