如何在 Haskell 函数中将随机数用作 Double?

How can a random number be used as a Double in a Haskell function?

我正在尝试编写一个函数,该函数使用随机数作为条件来与列表(通过将函数映射到整数范围来创建)进行比较。我正在以交互方式进行操作,如果我通过分别定义每个术语来进行操作,它会起作用:

import System.Random.MWC (create)
import Statistics.Distribution (genContVar)
import Statistics.Distribution.Uniform (uniformDistr)

rng <- create
rd <- (genContVar (uniformDistr 0 1)) rng

f x = takeWhile (<rd) $ fmap (*x) [1..10]

或者,我可以使用带有 let 表达式的非随机 Double,也没有问题

f x = let rd = 0.4 in takeWhile (<rd) $ fmap (*x) [1..10]

但是,如果我尝试将它们放在一起,则会出现错误

f x = let rand <- (genContVar (uniformDistr 0 1) g) in takeWhile (<rand) $ fmap (*x) [1..10]

<interactive>:39:16: error:
parse error on input ‘<-’
Perhaps this statement should be within a 'do' block?

我知道拥有不同的变量类型会阻止相加、Int 和 Double,而且 monad 非常特殊,但作为 Haskell 的新手,我希望避免更广泛的哲学现在尝试找到一种在一般函数中使用随机数的实用方法。

当您在 GHCi 中计算表达式时,您已经在 IO monad 中。这就是 OP GCHi 代码起作用的原因。

正如 n.m. 在评论中指出的那样,单子 使用随机数(以及所有Haskell 中的其他非确定性或有效行为)。如果您想编写 Haskell 代码,您迟早必须了解它们是什么。不过,好消息是学习 monad 并不像有些人认为的那么难。

Haskell 确实以 do 符号的形式为 monad 提供语法糖。使用它,您可以编写如下函数:

f x = do
  rng <- create
  rd <- (genContVar (uniformDistr 0 1)) rng

  return $ takeWhile (<rd) $ fmap (*x) [1..10]

此函数的类型为 PrimMonad m => Double -> m [Double]PrimMonad 在输出类型中的存在意味着某种效果。该函数可以(并且很可能)是不纯的。

理论上,可以像上面那样使用不纯的语言结构来编写整个 Haskell 程序,但是 Haskell 的要点是尽可能多地保持代码的纯净,所以应尽可能限制不纯代码的使用。