2014-02-02 3 views
4

Я только что начал изучать Haskell с приятным чтением «Учите вас Haskell для отличного блага!» и в книге есть пример, который для меня не имеет смысла. Это говорит о том, что следующий код будет выводить ту же случайную строку дважды:Использование глобального генератора случайных чисел через getStdGen в Haskell

main = do 
    gen <- getStdGen 
    putStrLn $ take 20 (randomRs ('a','z') gen) 
    gen2 <- getStdGen 
    putStrLn $ take 20 (randomRs ('a','z') gen2) 

С другой стороны, если та же программа называется два раза, несомненно, дают различные выходы. Кроме того, что, кажется, не соответствует, если я сравниваю его с кодом ниже, который дает различные значения s1 и s2:

main = do 
    s1 <- getLine 
    s2 <- getLine 
    putStrLn s1 
    putStrLn s2 

Я задаюсь вопросом, как вышеупомянутые два примера различны.

ответ

6

Это только определенное свойство getStdGen: в отличие от getLine, это не «эффективное» действие IO, а просто доступ к одному и тому же состоянию генератора случайных генераторов при каждом его вычислении. Таким образом, это почти чистая функция, но поскольку она будет отличаться между различными прогонами программы (и даже в том же прогоне, если вы явно меняете с помощью setStdGen), они тем не менее поместили ее в монаду IO.

Возможно, хорошая аналогия такова:

main = do 
    file <- readFile "/etc/bash.bashrc" -- or any other persistent system file 
    putStrLn $ head (lines file) 
    file2 <- readFile "/etc/bash.bashrc" 
    putStrLn $ head (lines file2) 
+0

Вызов 'randomRIO' или' randomIO' также должен быть изменен. – Carl

+0

It * is * effectful, и когда вы ставите 'setStdGen' между ними, он возвращает другой случайный генератор. – Ingo

3

Глядя на source:

-- |Gets the global random number generator. 
getStdGen :: IO StdGen 
getStdGen = readIORef theStdGen 

theStdGen :: IORef StdGen 
theStdGen = unsafePerformIO $ do 
    rng <- mkStdRNG 0 
    newIORef rng 

mkStdRNG :: Integer -> IO StdGen 
mkStdRNG o = do 
    ct   <- getCPUTime 
    (sec, psec) <- getTime 
    return (createStdGen (sec * 12345 + psec + ct + o)) 

Так что с практической точки зрения, это просто сделать с реализацией getStdGen: он читает один IORef который инициализируется один раз при первом использовании в заданном вызове программы.

Смежные вопросы