我想创建一个Haskell函数,可以从给定列表中选择一个随机数。 我的签名是:
randomPick :: [a] -> a
我该怎么办?
答案 0 :(得分:26)
Haskell中“纯”函数的部分定义是 referentially transparent ,也就是说,可以与评估它的结果互换。这意味着每次评估它的结果必须相同。我想,你想要的功能是不可能的,我担心。要在Haskell中生成随机数,函数需要执行以下两项操作之一:
获取并返回伪随机数生成器,例如:
randomPick :: RNG -> [a] -> (a, RNG)
或使用IO
访问“外部世界”的随机性:
randomPick :: [a] -> IO a
这两种风格均由the module System.Random
提供。此外,在前一种情况下,通过PRNG可以使用State
monad或special-purpose Random monad抽象出来。
答案 1 :(得分:22)
您所描述的内容无法在纯功能代码中完成。
纯函数代码意味着每次都会为同一输入获得相同的输出。由于随机函数根据定义为同一输入提供了不同的输出,因此在纯函数代码中这是不可能的。
除非您按照@camccann's answer中的说明传递额外的值。从技术上讲,它甚至不必像RNG那样先进,具体取决于您的需求。你可以传递一个整数,然后乘以10并减去3(或其他),然后取模数来找到你的索引。然后你的功能仍然是纯粹的,但你可以直接控制随机性。
另一种选择是使用RandomRIO
生成范围内的数字,然后您可以使用该数字从列表中选择索引。这将要求您输入IO monad。
答案 2 :(得分:5)
如果你想在纯功能代码中使用随机数生成器,但不必显式传递生成器状态,那么你可以使用状态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