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

use*_*662 21 haskell

我想创建一个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 .

  • 是的,确实看到MonadRandom(在这个答案中链接) - 我特地来到这里提到它,如果它还没有在这里. (4认同)
  • 您还可以使用QuickCheck的"Gen"monad作为方便的现成实现.除此之外,它已经实现了所需的功能. (2认同)

Mar*_*off 23

您所描述的内容无法在纯功能代码中完成.

纯函数代码意味着每次都会为同一输入获得相同的输出.由于随机函数根据定义为同一输入提供了不同的输出,因此在纯函数代码中这是不可能的.

除非你传递额外的值,如@ camccann的回答中所述.从技术上讲,它甚至不必像RNG那样先进,具体取决于您的需求.你可以传递一个整数,然后乘以10并减去3(或其他),然后取模数来找到你的索引.然后你的功能仍然是纯粹的,但你直接控制随机性.

另一个选项是用于RandomRIO生成范围中的数字,然后您可以使用该数字从列表中选择索引.这将要求您输入IO monad.

  • 不,那也不可能;“ makeIO :: [a]-> IO [a]”只是“ return”,而类型“ IO a-> a”没有任何功能-输入后就无法离开IO monad。正如Mark所说,这是因为Haskell是纯粹的:您不能更改变量的值,因此,每次给函数一个输入时,它都必须始终返回相同的输出。由于“ IO”是表示动作的纯粹方式,当采取这些动作时,它们可能(看起来像它们)使事物发生变异,因此它必须是不可破坏的,这是由于其定义被隐藏并且缺少“ IO”的功能而得以实施a-> a`。 (2认同)

snk*_*kid 7

如果你想在纯函数代码中使用随机数生成器但不必显式传递生成器状态,那么你可以使用状态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)