Haskell中的随机整数 [英] Random Integer in Haskell

查看:95
本文介绍了Haskell中的随机整数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习Haskell并学习我想要生成一个随机的Int类型。我很困惑,因为下面的代码工作。基本上,我想要一个Int而不是IO Int。

I am in the process of learning Haskell and to learn I want to generate a random Int type. I am confused because the following code works. Basically, I want an Int not an IO Int.

在ghci中,这是有效的:

In ghci this works:

Prelude> import System.Random
Prelude System.Random> foo <- getStdRandom (randomR (1,1000000))
Prelude System.Random> fromIntegral foo :: Int
734077
Prelude System.Random> let bar = fromIntegral foo :: Int
Prelude System.Random> bar
734077
Prelude System.Random> :t bar
bar :: Int

所以当我尝试用do它失败了,我不明白为什么。

So when I try to wrap this up with do it fails and I don't understand why.

randomInt = do
    tmp <- getStdRandom (randomR (1,1000000))
    fromIntegral tmp :: Int

编译器产生以下内容: p>

The compiler produces the following:

Couldn't match expected type `IO b0' with actual type `Int'
In a stmt of a 'do' block: fromIntegral tmp :: Int
In the expression:
  do { tmp <- getStdRandom (randomR (1, 1000000));
         fromIntegral tmp :: Int }
In an equation for `randomInt':
    randomInt
      = do { tmp <- getStdRandom (randomR (1, 1000000));
               fromIntegral tmp :: Int }
Failed, modules loaded: none.

我是Haskell的新手,所以如果有更好的方法来生成一个随机的Int,将是首选。

I am new to Haskell, so if there is a better way to generate a random Int without do that would be preferred.

所以我的问题是,为什么我的函数不起作用,有没有更好的方法来获得一个随机Int。

So my question is, why does my function not work and is there a better way to get a random Int.

推荐答案

简单的答案是,如果不调用一些IO,就不能生成随机数。无论何时您获得标准生成器,您都必须与主机操作系统进行交互,并创建一个新的种子。正因为如此,任何生成随机数的函数都有非确定性(该函数为相同的输入返回不同的值)。这就像是希望能够从用户那里获得来自用户的输入,而不需要它在IO monad中。

The simple answer is that you can't generate random numbers without invoking some amount of IO. Whenever you get the standard generator, you have to interact with the host operating system and it makes a new seed. Because of this, there is non-determinism with any function that generates random numbers (the function returns different values for the same inputs). It would be like wanting to be able to get input from STDIN from the user without it being in the IO monad.

相反,您有几个选项。您可以将所有依赖随机生成值的代码编写为纯函数,并仅执行IO来获取 main 或类似函数中的标准生成器,或者可以使用 MonadRandom 软件包,可为您提供预建monad 用于管理随机值。由于大多数 System.Random 中的每个纯函数都接收一个生成器并返回一个包含随机值和一个新生成器的元组,所以 Rand monad将这种模式抽象出来,这样您就不必担心它了。您最终可以编写代码,如

Instead, you have a few options. You can write all your code that depends on a randomly generated value as pure functions and only perform the IO to get the standard generator in main or some similar function, or you can use the MonadRandom package that gives you a pre-built monad for managing random values. Since most every pure function in System.Random takes a generator and returns a tuple containing the random value and a new generator, the Rand monad abstracts this pattern out so that you don't have to worry about it. You can end up writing code like

import Control.Monad.Random hiding (Random)
type Random a = Rand StdGen a

rollDie :: Int -> Random Int
rollDie n = getRandomR (1, n)

d6 :: Random Int
d6 = rollDie 6

d20 :: Random Int
d20 = rollDie 20

magicMissile :: Random (Maybe Int)
magicMissile = do
    roll <- d20
    if roll > 15
        then do
            damage1 <- d6
            damage2 <- d6
            return $ Just (damage1 + damage2)
        else return Nothing

main :: IO ()
main = do
    putStrLn "I'm going to cast Magic Missile!"
    result <- evalRandIO magicMissile
    case result of
        Nothing -> putStrLn "Spell fizzled"
        Just d  -> putStrLn $ "You did " ++ show d ++ " damage!"

还有一个随附的monad变压器,但我会坚持下去,直到您掌握好在monads上。比较此代码以使用 System.Random

There's also an accompanying monad transformer, but I'd hold off on that until you have a good grasp on monads themselves. Compare this code to using System.Random:

rollDie :: Int -> StdGen -> (Int, StdGen)
rollDie n g = randomR (1, n) g

d6 :: StdGen -> (Int, StdGen)
d6 = rollDie 6

d20 :: StdGen -> (Int, StdGen)
d20 = rollDie 20

magicMissile :: StdGen -> (Maybe Int, StdGen)
magicMissile g =
    let (roll, g1) = d20 g
        (damage1, g2) = d6 g1
        (damage2, g3) = d6 g2
    in if roll > 15
        then (Just $ damage1 + damage2, g3)
        else Nothing

main :: IO ()
main = do
    putStrLn "I'm going to case Magic Missile!"
    g <- getStdGen
    let (result, g1) = magicMissile g
    case result of
        Nothing -> putStrLn "Spell fizzled"
        Just d  -> putStrLn $ "You did " ++ show d ++ " damage!"

在这里我们必须手动管理发生器的状态,而且我们没有得到方便使我们的执行顺序更加清晰的注释(懒惰有助于第二种情况,但它使它更加令人困惑)。手动管理这个状态是无聊,乏味,容易出错的。 兰德 monad使得一切变得更加简单,更加清晰,并且减少了错误发生的几率。这通常是在Haskell中进行随机数生成的首选方式。

Here we have to manually have to manage the state of the generator and we don't get the handy do-notation that makes our order of execution more clear (laziness helps in the second case, but it makes it more confusing). Manually managing this state is boring, tedious, and error prone. The Rand monad makes everything much easier, more clear, and reduces the chance for bugs. This is usually the preferred way to do random number generation in Haskell.

值得一提的是,您实际上可以展开一个 IO a 值只是一个 a 值,但除非您100%确定您知道你在做什么。有一个名为 unsafePerformIO 的函数,顾名思义它是不安全的。它存在于Haskell中,主要用于当您与FFI进行交互时,例如使用C DLL。假设任何外部函数默认执行 IO ,但如果您确定地知道您调用的函数没有副作用,那么可以安全地使用 unsafePerformIO 。任何其他时间只是一个坏主意,它可能会导致代码中出现一些非常奇怪的行为,这些行为几乎不可能追踪到。

It is worth mentioning that you can actually "unwrap" an IO a value to just an a value, but you should not use this unless you are 100% sure you know what you're doing. There is a function called unsafePerformIO, and as the name suggests it is not safe to use. It exists in Haskell mainly for when you are interfacing with the FFI, such as with a C DLL. Any foreign function is presumed to perform IO by default, but if you know with absolute certainty that the function you're calling has no side effects, then it's safe to use unsafePerformIO. Any other time is just a bad idea, it can lead to some really strange behaviors in your code that are virtually impossible to track down.

这篇关于Haskell中的随机整数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆