9

次のプログラムは正しく終了します。

import System.Random

randomList = mapM (\_->getStdRandom (randomR (0, 50000::Int))) [0..5000]

main = do
  randomInts <- randomList
  print $ take 5 randomInts

ランニング:

$ runhaskell test.hs
[26156,7258,29057,40002,26339]

ただし、無限のリストをフィードすると、プログラムが終了することはなく、コンパイルすると、最終的にスタックオーバーフローエラーが発生します。

import System.Random

randomList = mapM (\_->getStdRandom (randomR (0, 50000::Int))) [0..]

main = do
  randomInts <- randomList
  print $ take 5 randomInts

ランニング、

$ ./test
Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize -RTS' to increase it.

getStdRandom私は、リストからアイテムを選ぶたびに、プログラムが怠惰に評価し、5回行った後に終了することを期待していました。リスト全体を評価しようとしているのはなぜですか?

ありがとう。

乱数の無限のリストを取得するためのより良い方法はありますか?このリストを純粋関数に渡したい。

編集:もう少し読んで、関数が

randomList r = do g <- getStdGen
                  return $ randomRs r g

私が探していたものです。

EDIT2:camccannの答えを読んだ後、私はそれgetStdGenがすべての呼び出しで新しいシードを取得していることに気づきました。代わりに、この関数を単純なワンショットランダムリストジェネレーターとして使用することをお勧めします。

import System.Random

randomList :: Random a => a -> a -> IO [a]
randomList r g = do s <- newStdGen
                    return $ randomRs (r,g) s

main = do r <- randomList 0 (50::Int)
          print $ take 5 r

mapMしかし、なぜ通話が終了しなかったのかはまだわかりません。明らかに乱数とは関係ありませんが、mapM多分何か関係があります。

たとえば、次のことも終了しないことがわかりました。

randomList = mapM (\_->return 0) [0..]

main = do
  randomInts <- randomList
  print $ take 50000 randomInts

何が得られますか?ちなみに、私見、上記のrandomInts関数はにあるはずですSystem.RandomIOモナドでランダムリストを非常に簡単に生成し、必要に応じてそれを純粋関数に渡すことができると非常に便利です。これが標準ライブラリに含まれてはならない理由がわかりません。

4

2 に答える 2

12

一般に乱数は厳密ではありませんが、モナディックバインディングは厳密ではありません。ここでの問題はmapM、リスト全体をシーケンス処理する必要があることです。その型署名を考えてみましょう(a -> m b) -> [a] -> m [b]。これが意味するように、それが行うことは、最初mapに型[a]のリストを型のリストに入れ[m b]、次にsequenceそのリストを使って型の結果を取得することですm [b]。したがって、適用の結果をバインドする場合mapMたとえばの右側に配置する<-場合、これは「この関数をリストにマップしてから、各モナディックアクションを実行し、結果を1つのリストに結合する」ことを意味します。 。リストが無限の場合、これはもちろん終了しません。

単に乱数のストリームが必要な場合は、各番号にモナドを使用せずにリストを生成する必要があります。使用しているデザインを使用した理由は完全にはわかりませんが、基本的な考え方は次のとおりです。シード値を指定して、疑似乱数ジェネレーターを使用して、1)乱数2)新しいシードのペアを生成します。 、次に新しいシードで繰り返します。もちろん、任意のシードは毎回同じシーケンスを提供します。したがって、モナドgetStdGenに新鮮なシードを提供する関数を使用できます。IO次に、そのシードを使用して、完全に純粋なコードで無限のシーケンスを作成できます。

実際、System.Randomまさにその目的のために、randomsまたはandrandomRsの代わりに関数を提供します。randomrandomR

何らかの理由で自分でやりたい場合は、基本的に展開する必要があります。unfoldrからの関数Data.Listには型シグネチャ(b -> Maybe (a, b)) -> b -> [a]がありますが、これはかなり自明です。型の値が与えられるとb、関数を適用して、型の何かと型aの新しいジェネレータ値を取得するbNothing、シーケンスの終わりを示します。

無限のリストが必要なので、を返す必要はありませんNothing。したがって、部分的randomRに目的の範囲に適用し、それを構成すると、次のようになりJustます。

Just . randomR (0, 50000::Int) :: (RandomGen a) => a -> Maybe (Int, a)

これをフィードすると、次のようになりunfoldrます。

unfoldr (Just . randomR (0, 50000::Int)) :: (RandomGen a) => a -> [Int]

...これは主張どおりに機能します。のインスタンスが与えられると、そのシードから生成された乱数RandomGenの無限の(そして怠惰な)リストが生成されます。

于 2010-07-29T02:22:26.230 に答える
6

私はこのようなことをもっと行い、randomRに最初のRandomGenで作業をさせます。

#! /usr/bin/env runhaskell

import Control.Monad
import System.Random


randomList :: RandomGen g => g -> [Int]
randomList = randomRs (0, 50000)

main :: IO ()
main = do
   randomInts <- liftM randomList newStdGen
   print $ take 5 randomInts

怠惰に関しては、ここで起こっているのはそれmapMです(sequence . map)

そのタイプは次のとおりです。mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]

関数をマッピングし、を与えて[m b]から、これらすべてのアクションを実行してを作成する必要がありm [b]ます。これは、無限のリストを通過することのないシーケンスです。

これは、前の質問への回答でよりよく説明されています:HaskellのmapMは怠惰ではありませんか?

于 2010-07-29T02:20:58.620 に答える