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