如何在纯函数中选择一个随机列表元素?

时间:2010-05-28 02:11:21

标签: haskell

我想创建一个Haskell函数,可以从给定列表中选择一个随机数。 我的签名是:

randomPick :: [a] -> a 

我该怎么办?

3 个答案:

答案 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