Haskellのランダムについて

Hakellは関数型言語で参照透過性とかを保証するために、同じことをやったら同じ結果が出ることが保証されているために、randomについてはちょっとというか相当ややこしい。


いまの私の理解は以下のようなものですが、これで合ってるか。

 

import System.Random
let dice=do;n<-getStdRandom(randomR(1,6::Int));return n

 

として、diceと打ち込むと1から6の範囲でランダムに数字を返してくれるので一見ランダムに見える。
がしかし、これは数字でないのでそのままでは使い物にならない。

 

dice + 3 -- > errorになる
n = dice -- > errorになる

 

タイプを調べてみると

 

:t dice -- > IO Int

 

となって、ただのIntに変換するためには一旦再度IOを通す必要がある。

 

n <- dice

 

とすると数字になる。


リストにしてみたらどうか。

import Control.Monad
replicateM 10 $ getStdRandom(randomR(1,6::Int))
-- > [5,6,5,4,4,2,3,5,1,6]

 

とかも IO [Int]なので、sumすることもできない。sumするためには

 

list <- replicateM 10 $ getStdRandom(randomR(1,6::Int))
list -- > [1,6,6,3,6,2,4,3,5,2]
sum list -- > 38


ということ。


Haskellは同じランダムのたねを入れるとランダムの返す値が同じになる。
なので、setStdGenで毎回同じタネを入れると同じランダム値になってしまう。

 

setStdGen(mkStdGen 12)
getStdRandom(randomR(0, 1.0))
0.13293852220286906
setStdGen(mkStdGen 12)
getStdRandom(randomR(0, 1.0))
0.13293852220286906

 

getStdGenで生成するStdGenは起動時にセットされたものを返してくるので、使うたびにStdGenを書き換えるnewStdGenとか、getStdRandomを使う必要がある。

 

getStdGenを使うとこんな感じです。ランダムな値が全部同じになってしまいます。
gen <- getStdGen
take 10 $ randomRs (1,6) gen
[1,6,5,1,3,5,6,5,5,4]
take 10 $ randomRs (1,6) gen
[1,6,5,1,3,5,6,5,5,4]
gen <- getStdGen
take 10 $ randomRs (1,6) gen
[1,6,5,1,3,5,6,5,5,4]

 

newStdGenを使うと次のように毎回違った値になりますが、やはり毎回呼び出す必要がある。


gen <- newStdGen
take 10 $ randomRs (1,6) gen
[4,1,3,3,4,2,3,2,6,4]
gen <- newStdGen
take 10 $ randomRs (1,6) gen
[5,2,4,2,3,3,3,5,1,6]
gen <- newStdGen
take 10 $ randomRs (1,6) gen
[1,2,2,2,4,3,3,4,3,3]
gen <- newStdGen
take 10 $ randomRs (1,6) gen
[3,1,1,3,5,1,5,5,6,6]

 

これだとIO Intではなくてただのリスト[Int]なので扱いは楽です。

 

ラムダ式にくるんで、ひとつの関数内でIO IntからIntにできないかと考えたのですが、できませんでした。できたら関数プログラミングでなくなってしまう。かも。

 

いまの結論:


newStdGenを使う。毎回呼び出す。gen <- newStdGen。
randomRsを使う。

 

問題1: '0123456789ABCDEEFGHIJKLMNOPQRSTUVWXYZ'内の文字をランダムに並べる。
問題2: 'あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわゐゑをん'をランダムに用いて5,7,5の俳句をつくる。

 

問題1: 解答例

-- random36.hs
-- runghc rndom36.hs

import System.Random
import Data.List
import Control.Monad

main :: IO ()
main = do
    gen <- newStdGen
    let randomlist = take 30 $ randomRs (0,35::Int) gen
    let result = map (str!!) randomlist
    putStrLn result

str="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"

 -- 出力例

Z8CIZZB9OTO6RDTHI2YW0A0LN9RFU2
R6PZKKZ4MVJFKI8ALNU7GGWXB48987

 

 

問題2: 解答例

-- randomhaiku.hs
-- runghc randomhaiuku.hs

import System.Random
import Data.List
import Control.Monad

main :: IO ()
main = do
    gen <- newStdGen
    let first = map (str!!) $ take 5 $ randomRs (0, 47::Int) gen
    gen <- newStdGen
    let second = map (str!!) $ take 7 $ randomRs (0, 47::Int) gen
    gen <- newStdGen
    let third = map (str!!) $ take 5 $ randomRs (0, 47::Int) gen
    let result = unwords [first, second, third]
    putStrLn result

str="あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわゐゑをん"

-- 作品例
-- やふるれな えかねもひんは よゆいのて
-- はゐへおき れつしゐきによ ぬきよるお
-- ゆんのしは おはすおはねせ らそむあね
-- ねろれすと りなのかとおな なえうわつ
-- ゆつゆのき おらとちすへち らふゑすお
-- りんうなあ くとりせきはり ゆこさけを
-- いははつろ つえけころれや をももかる
-- ぬんみやい らなとへいゐや とこゑねれ
-- やあなもも おふえふもりれ むちほつひ
-- ゑとしゐろ せをあよろめか んやとむる
-- ねかもはさ りみねてさやゆ はたろせう
-- むそうへめ いもらにりとけ ほのさちみ
-- むみうおわ いえりゐわなり ほれさるや
-- わほゆうみ しいはわみきと ゑりえりほ
-- ねうぬへん りへかにんころ はすみちゑ
-- てえならち もほえみちろう たてほひそ