如何在Haskell中生成不同的随机值?

Aug*_*ias 0 random haskell

假设我有一个这样的列表:

let list = ["random", "foo", "random", "bar", "random", "boo"]
Run Code Online (Sandbox Code Playgroud)

我想遍历一个列表并将所有“随机”元素映射到不同的随机字符串:

let newList = fmap randomize list
print newList
-- ["dasidias", "foo", "gasekir", "bar", "nabblip", "boo"]
Run Code Online (Sandbox Code Playgroud)

我的随机函数看起来像这样:

randomize :: String -> String
randomize str = 
  case str of
    "random" -> randStr
    _        -> str
  where
    randStr = take 10 $ randomRs ('a','z') $ unsafePerformIO newStdGen
Run Code Online (Sandbox Code Playgroud)

但是对于每个“随机”元素,我都会得到相同的随机字符串:

["abshasb", "foo", "abshasb", "bar", "abshasb", "boo"]
Run Code Online (Sandbox Code Playgroud)

我不知道为什么会这样,以及如何为每次出现的“随机”获得不同的随机值。

Dan*_*ner 7

您的代码有两个问题:

  1. 您正在调用unsafePerformIO,但明显违反了该功能的约定。您必须证明提供给您的东西unsafePerformIO实际上是纯净的,并且编译器在其权限范围内,就像是在那样做,在这里绝对不是。
  2. 使用后,您没有仔细跟踪更新的随机数生成器的状态。实际上,不可能用randomRs; 来正确地做到这一点。如果使用randomRs,则近似为阶近似值,它必须是程序所需的最后一个随机性。

解决这两个问题的最简单方法就是承认您确实在做IO。所以:

import Control.Monad
import System.Random

randomize :: String -> IO String
randomize "random" = replicateM 10 (randomRIO ('a', 'z'))
randomize other = pure other
Run Code Online (Sandbox Code Playgroud)

在ghci中尝试一下:

> traverse randomize ["random", "foo", "random", "bar", "random", "boo"]
["xytuowzanb","foo","lzhasynexf","bar","dceuvoxkyh","boo"]
Run Code Online (Sandbox Code Playgroud)

没有人打来电话unsafePerformIO,也没有推脱的举证责任;并randomRIO在hidden中为您跟踪更新的生成器状态IORef,因此您可以在每次调用时正确地继续前进。

  • @AugustoDias不,没有正确的方法以`[String]`结尾并且没有其他上下文。您可以以“ StdGen->(StdGen,[String])”结尾或其他同构的东西。这是您可以获得的最接近纯净的颜色。 (5认同)
  • @AugustoDias强烈不同意:当事情的行为取决于我(调用者)无法控制的价值观时,我很清楚地做清楚的广告。我大约每天在工作时使用它;我能想到的只有很少几件事比评估实用性更符合“实践”要求。 (3认同)
  • @AugustoDias允许IO [String]每次生成不同的字符串。`[String]`不是-它是给定的,不可变的字符串列表。如果要打印`x :: IO [String]`,则可以使用`x >> = traverse putStrLn`之类的东西(或者更好的是,在正确的导入之后使用`traverse_`)。我建议阅读有关IO在Haskell中的工作方式的知识,'net上应该有很多教程。 (2认同)