Haskell:随机数 - IO操作的Int

4

最近我开始学习Haskell,但是它的语法对我来说很困惑。我正在尝试使用以下代码获得0到51之间的随机数:

randomRIO (0, 51)

但是我不知道如何将它变成实际的 Int(而不是 IO Int)。

我相信你应该能够做到:

gen <- randomRIO (0, 51)

然后 gen 应该是一个 Int,但我必须在一个 do 块中执行 - 我该如何创建一个函数,它接受此参数并返回一个 Int

通过谷歌搜索,我担心有些基础知识我没有理解。我完全迷失了方向。


do 块中的下一行可能是类似于 print (yourFunc gen) 的内容。 - 4castle
1个回答

17

没错,有些基础概念是新手 Haskeller 经常遇到的问题。基本上,你所说的一切都是正确的,如果你想要一个返回随机整数的“函数”,那是不可能的。

你可以这样做:

main :: IO ()
main = do
    num <- getRandomR (0, 51)
    putStrLn ("The number is " ++ show num)

你也可以将其拆分出来

getCardIndex :: IO Int
getCardIndex = do
    ix <- getRandomR (0,51)
    return (ix + 1)   -- just as an example of how you can transform it

main :: IO ()
main = do
    card <- getCardIndex
    putStrLn ("The number is " ++ show card)

你无法将其变成一个纯函数/值:
pureCardIndex :: Int
pureCardIndex = ioToPure (getRandomR (0,51)) -- NOT POSSIBLE

由于没有像 ioToPure 这样的(伦理)功能。一个纯函数必须对于相同的输入始终返回相同的输出,而这正是你不希望随机数函数做到的。所以我们必须与其一起使用 IO。一旦你用 IO 生成了一个随机数,任何依赖于该随机数的东西也必须在 IO 中。

人们很快就会开始解释单子理论等等,这是很好的东西,我只建议耐心地接触这些模式,先熟悉它们再深入理论方面。


8
@ftor 这不是“unsafePerformIO”的适当使用方式,我认为大多数社区成员都会同意我的看法。在Haskell中工作时,我们对任何函数的首要期望是引用透明性,这给了我们所钟爱的自由进行等式推导和重构而不需要费太多脑筋。当破坏引用透明性被重新封装以保持接口纯粹时,“unsafePerformIO”是适当的。 - luqui
3
@ftor提到unsafePerformIO用于在函数是纯函数但无法被类型系统证明的情况下。一个很好的例子是使用FFI调用你知道是纯函数的函数。随机数生成绝对是有副作用的,因此应该在IO内完成。 - TheInnerLight
@ftor 在这种情况下并不是教条主义;否则做法会直接破坏语言的语义,并导致不可预测的结果。 - Cubic
6
在一个纯函数式编程语言中,如果rand :: Integer -> Integer,那么编译器可以将let x = rand 6 in x + x内联为rand 6 + rand 6。但是,如果rand 6是掷骰子的结果,则这种重写是不正确的,因为前者永远不会等于7,例如。调试此问题很容易让人疯狂,因为问题不在代码中,而是由编译器引入的。编译器没有错,因为它依赖于程序员保证的谎言rand :: Integer -> Integer--这才是真正的问题。 - chi
1
很棒的例子,@chi。编译器本身进行等式推理,这只适用于引用透明性。我应该考虑到这一点。 - user6445533
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接