我想创建一个Haskell函数,可以从给定列表中选择一个随机数.我的签名是:
randomPick :: [a] -> a
Run Code Online (Sandbox Code Playgroud)
我该怎么办?
C. *_*ann 28
Haskell中"纯"函数的部分定义是它是引用透明的,即可以与评估它的结果互换.这意味着每次评估它的结果必须相同.我想,你想要的功能是不可能的,我担心.要在Haskell中生成随机数,函数需要执行以下两项操作之一:
获取并返回伪随机数生成器,例如:
randomPick :: RNG -> [a] -> (a, RNG)
Run Code Online (Sandbox Code Playgroud)
或者IO用来访问"外部世界"的随机性:
randomPick :: [a] -> IO a
Run Code Online (Sandbox Code Playgroud)
这两种样式都由模块System.Random提供.此外,在前一种情况下,可以使用Statemonad或者特殊用途的随机monad来抽象传递PRNG .
Mar*_*off 23
您所描述的内容无法在纯功能代码中完成.
纯函数代码意味着每次都会为同一输入获得相同的输出.由于随机函数根据定义为同一输入提供了不同的输出,因此在纯函数代码中这是不可能的.
除非你传递额外的值,如@ camccann的回答中所述.从技术上讲,它甚至不必像RNG那样先进,具体取决于您的需求.你可以传递一个整数,然后乘以10并减去3(或其他),然后取模数来找到你的索引.然后你的功能仍然是纯粹的,但你直接控制随机性.
另一个选项是用于RandomRIO生成范围中的数字,然后您可以使用该数字从列表中选择索引.这将要求您输入IO monad.
如果你想在纯函数代码中使用随机数生成器但不必显式传递生成器状态,那么你可以使用状态monad(或monad转换器)并隐藏管道.国家monad仍然是公民透明的,逃离国家monad是安全和正常的.如果你想要真正的本地可变安全,你可以使用ST monad,它在外面是纯功能的.
这是我编写和使用的一些有用的代码:
rand :: (Random a, RandomGen g, MonadState g m) => a -> a -> m a
rand lo hi = do
r <- get
let (val, r') = randomR (lo, hi) r
put r'
return val
Run Code Online (Sandbox Code Playgroud)