fugafuga.write

日々のログ



すごいH本 part65

もっともっとランダム関数

コインを投げる話の続き。

もっとたくさんのランダム値が欲しい場合どうするか

randoms を使う

*Main System.Random> :t randoms
randoms :: (RandomGen g, Random a) => g -> [a]

乱数ジェネレータを受け取って無限長のランダム値のリストを返す。

*Main System.Random> take 5 $ randoms (mkStdGen 100) :: [Int]
[-3633736515773289454,-1610541887407225575,4434840125058622350,1116419036860971948,1434273519690261584]
*Main System.Random> take 5 $ randoms (mkStdGen 100) :: [Bool]
[True,False,False,False,False]

randoms の実装はこんな感じでできる

randoms' :: (RandomGen g, Random a) => g -> [a]
randoms' gen = let (value, newGen) = random gen in value:randoms' newGen

let の中で value と newGen を受け取って、value をリストの先頭につけて再帰。 これで無限リストが作れる。

有限のリストと新しいジェネレータを生成する関数はこう実装する

finiteRandoms :: (RandomGen g, Random a, Num n) => n -> g -> ([a], g)
finiteRandoms 0 gen = ([], gen)
finiteRandoms n gen =
    let (value, newGen) = random gen
        (restOfList, finalGen) = finiteRandoms (n-1) newGen
    in (value:restOfList, finalGen)

実行

Prelude System.Random> :l three-coins.hs
[1 of 1] Compiling Main             ( three-coins.hs, interpreted )

three-coins.hs:14:15: error:
    • Could not deduce (Eq n) arising from the literal ‘0’
      from the context: (RandomGen g, Random a, Num n)
        bound by the type signature for:
                   finiteRandoms :: (RandomGen g, Random a, Num n) =>
                                    n -> g -> ([a], g)
        at three-coins.hs:13:1-69
      Possible fix:
        add (Eq n) to the context of
          the type signature for:
            finiteRandoms :: (RandomGen g, Random a, Num n) =>
                             n -> g -> ([a], g)
    • In the pattern: 0
      In an equation for ‘finiteRandoms’: finiteRandoms 0 gen = ([], gen)
Failed, modules loaded: none.

エラーが発生した。 finiteRandoms関数のパターンマッチで 0 と Num a を比較しようとしている部分でエラーが発生している模様。 メッセージにあるように、Eq n の制約を追加する。

finiteRandoms :: (RandomGen g, Random a, Num n, Eq n) => n -> g -> ([a], g)
finiteRandoms 0 gen = ([], gen)
finiteRandoms n gen =
    let (value, newGen) = random gen
        (restOfList, finalGen) = finiteRandoms (n-1) newGen
    in (value:restOfList, finalGen)

再度実行

Prelude System.Random> :l three-coins.hs
[1 of 1] Compiling Main             ( three-coins.hs, interpreted )
Ok, modules loaded: Main.
*Main System.Random> take 5 $ randoms' (mkStdGen 100)
[-3633736515773289454,-1610541887407225575,4434840125058622350,1116419036860971948,1434273519690261584]
*Main System.Random> take 5 $ randoms' (mkStdGen 100) :: [Int]
[-3633736515773289454,-1610541887407225575,4434840125058622350,1116419036860971948,1434273519690261584]
*Main System.Random> take 5 $ randoms' (mkStdGen 100) :: [Bool]
[True,False,False,False,False]
*Main System.Random> finiteRandoms 5 (mkStdGen 100) :: ([Bool], StdGen)
([True,False,False,False,False],942794516 652912057)
*Main System.Random> finiteRandoms 5 (mkStdGen 100) :: ([Int], StdGen)
([-3633736515773289454,-1610541887407225575,4434840125058622350,1116419036860971948,1434273519690261584],1772499918 2118231989)

できた。

ある範囲の乱数を生成したい場合は randomR を使う

*Main System.Random> :t randomR
randomR :: (RandomGen g, Random a) => (a, a) -> g -> (a, g)

実行

*Main System.Random> randomR (1, 10) (mkStdGen 400) :: (Int, StdGen)
(2,16045614 40692)
*Main System.Random> randomR (1, 10) (mkStdGen 121212) :: (Int, StdGen)
(4,555249856 40692)
*Main System.Random> randomR (1, 10) (mkStdGen 24242424) :: (Int, StdGen)
(5,1521307037 40692)
*Main System.Random> randomR (1, 10) (mkStdGen 595959) :: (Int, StdGen)
(5,224424247 40692)
*Main System.Random> randomR (1, 10) (mkStdGen 59595966) :: (Int, StdGen)
(6,966268608 40692)

randomRs もある。指定された範囲の乱数を無限に生成する。

*Main System.Random> :t randomRs
randomRs :: (RandomGen g, Random a) => (a, a) -> g -> [a]
*Main System.Random> take 5 $ randomRs (1, 10) (mkStdGen 121212)
[4,5,9,1,6]
*Main System.Random> take 5 $ randomRs (1, 10) (mkStdGen 56754399)
[7,1,8,7,6]
*Main System.Random> take 5 $ randomRs ('a', 'z') (mkStdGen 56754399) :: [Char]
"uwhsf"

所感

let を使った例を出してくれるので let の使い方の参考になる。 理解はできるが書けないと思うので多様な例を出してくれるのはありがたい。

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

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

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