fugafuga.write

日々のログ

すごいH本 part66

ランダム性とI/O

getStdGen という I/O アクションは、何らかの初期データを使って システムのグローバル乱数ジェネレータを初期化する。

import System.Random

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

実行

> stack ghc -- --make random_string.hs
[1 of 1] Compiling Main             ( random_string.hs, random_string.o )
Linking random_string ...
> ./random_string
pwmtcyvvuiqrlcxxugkg
> ./random_string
xgtscdkplkltwsviragk
> ./random_string
knqwbdakxwwngtwhfznj
> ./random_string
gaeihsegnztzrhktends

毎回違う値がとれている。

一度に2つのランダムな値が欲しいとする。

import System.Random

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

実行

> stack ghc -- --make random_string.hs                                        
[1 of 1] Compiling Main             ( random_string.hs, random_string.o )
Linking random_string ...
> ./random_string                                                             
ogifizssaztchwbwaavl
ogifizssaztchwbwaavl
> ./random_string                                                             
zyytwpyywogfjmtolvjm
zyytwpyywogfjmtolvjm
> ./random_string                                                             
lopqapxwdkakjuipcgfv
lopqapxwdkakjuipcgfv

getStdGen を2回実行しても同じグローバル乱数ジェネレータをシステムが 返してくるのでこれではダメ。

これを実現するためには、newStdGen アクションを使う。 これはグローバル乱数ジェネレータを2つのジェネレータに分割する。

import System.Random

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

実行

> stack ghc -- --make random_string.hs                                        
[1 of 1] Compiling Main             ( random_string.hs, random_string.o )
Linking random_string ...
> ./random_string                                                             
wxyevwiruiwsoqwyabdr
uwcvveeltuejritxceih
> ./random_string                                                             
zjqgfnuekjdaelehlxvj
goefmqrrebmzmqsiomao
> ./random_string                                                             
enxgkrluzhbyqoxhqssr
klekqhhqszulpjsntjsl

できた。

newStdGen を束縛すると、新しい乱数ジェネレータが 得られるだけでなく、グローバルジェネレータも更新される。

ユーザーにプログラムが考えた数を当てさせるプログラムを書く

import System.Random
import Control.Monad(when)

main = do
    gen <- getStdGen
    askForNumber gen

askForNumber :: StdGen -> IO ()
askForNumber gen = do
    let (randNumber, newGen) = randomR (1,10) gen :: (Int, StdGen)
    putStrLn "Which number in the range from 1 to 10 am I thinking of? "
    numberString <- getLine
    when (not $ null numberString) $ do
        let number = read numberString
        if randNumber == number
            then putStrLn "You are correct."
            else putStrLn $ "Sorry, it was " ++ show randNumber
        askForNumber newGen

実行

> stack ghc -- --make guess_the_number.hs                                     
[1 of 1] Compiling Main             ( guess_the_number.hs, guess_the_number.o )
Linking guess_the_number ...
> ./guess_the_number                                                          
Which number in the range from 1 to 10 am I thinking of?
1
Sorry, it was 7
Which number in the range from 1 to 10 am I thinking of?
2
Sorry, it was 5
Which number in the range from 1 to 10 am I thinking of?
3
Sorry, it was 1
Which number in the range from 1 to 10 am I thinking of?
4
Sorry, it was 1
Which number in the range from 1 to 10 am I thinking of?
5
Sorry, it was 6
Which number in the range from 1 to 10 am I thinking of?
6
Sorry, it was 9
Which number in the range from 1 to 10 am I thinking of?
7
Sorry, it was 6
Which number in the range from 1 to 10 am I thinking of?
5
Sorry, it was 10
Which number in the range from 1 to 10 am I thinking of?
3
Sorry, it was 9
Which number in the range from 1 to 10 am I thinking of?
2
Sorry, it was 1
Which number in the range from 1 to 10 am I thinking of?
1
Sorry, it was 5
Which number in the range from 1 to 10 am I thinking of?
2
Sorry, it was 10
Which number in the range from 1 to 10 am I thinking of?
3
Sorry, it was 7
Which number in the range from 1 to 10 am I thinking of?
4
Sorry, it was 5
Which number in the range from 1 to 10 am I thinking of?
5
Sorry, it was 10
Which number in the range from 1 to 10 am I thinking of?
6
You are correct.

なかなか当たらん。

所感

let number = read numberString でなんで数字に変換されるのか調べる。 デフォルトで文字列 -> 数値 の変換になるのかドキュメントのどこに書いてあるかわからん。

追記

型推論っぽい

Haskell の read 関数で、文字列から代数的データ型へ変換 - 導出インスタンスを使って | すぐに忘れる脳みそのためのメモ

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る