9

乱数を出力することになっている次のコードを検討してください。

import System.Random.Mersenne

main =
 do g <- (newMTGen Nothing)
    xs <- (randoms g) :: IO [Double]
    mapM_ print xs  

実行すると、セグメンテーション違反エラーが発生します。関数「ran​​doms」は無限リストを生成するため、これは当然のことです。xs の最初の 10 個の値だけを出力したいとします。どうすればそれができますか?xs は IO [Double] 型で、[IO Double] 型の変数が必要だと思います。2 つの間で変換するために存在する演算子。

4

2 に答える 2

11

xs の最初の 10 個の値だけを出力したいとします。どうすればそれができますか?

使用するだけtakeです:

main =
 do g <- (newMTGen Nothing)
    xs <- (randoms g) :: IO [Double]
    mapM_ print $ take 10 xs  

あなたが書いた

xs の型は IO [Double]

でも実際にrandoms gは has typeなのですが、 has typeという表記IO [Double]のおかげでそのまま当てはめられます。doxs[Double]take 10

次を使用してバインディングをスキップすることもできますliftM

main =
  do g <- newMTGen Nothing
     ys <- liftM (take 10) $ randoms g :: IO [Double]
     mapM_ print ys
于 2012-04-05T02:43:35.570 に答える
11

unsafeセグメンテーション違反エラーが発生し、名前にFFI または関数を使用していない場合、それどのような状況でも驚くことではありません! これは、GHC にバグがあるか、使用しているライブラリが安全でないことを行っていることを意味します。

Doubleを使用してs の無限リストを出力することmapM_ printはまったく問題ありません。リストは段階的に処理され、プログラムは一定のメモリ使用量で実行されます。使用しているモジュールにバグがあるSystem.Random.Mersenneか、それがベースにしている C ライブラリにバグがあるか、またはコンピュータの問題 (RAM の故障など) があると思われます。1newMTGenこの警告に付随する注意事項:

現在の SFMT ライブラリは非常に不純であるため、現在、プログラムごとに 1 つのジェネレータしか許可されていません。再初期化しようとすると失敗します。

代わりに、提供されているグローバルMTGenを使用する方がよい場合があります。

IO [Double]つまり、そのように変換することはできません[IO Double]。結果のリストがアクションを実行せずにどれくらいの長さになるかを知る方法はありませんIO。これは不可能です。純粋な結果があるためです (たまたまIOアクションが含まれている場合でも)。無限リストの場合、次のように記述できます。

desequence :: IO [a] -> [IO a]
desequence = desequence' 0
  where
    desequence n m = fmap (!! n) m : desequence (n+1) m

ただし、このリストのアクションを実行するたびに、IO [a]アクションが再度実行されます。リストの残りを破棄するだけです。

randoms機能して乱数の無限リストを返すことができる理由は、 で遅延 IO を使用するためunsafeInterleaveIOです。(名前に「危険」が含まれているにもかかわらず、これはセグメンテーション違反を引き起こすことはできないため、別のことが進行中であることに注意してください。)

1その他の可能性は低いですが、C ライブラリのコンパイルミスや GHC のバグが含まれます。

于 2012-04-04T19:30:14.557 に答える