11

まず、タイトルが意味不明で申し訳ありません。実際に何が起こっているのかわからないので、これ以上具体的にすることはできません。

さて、私の質問です。Haskellの 99 の問題のうち 23 の問題について、次のスニペットを実装しました。これはn、リストから項目をランダムに選択する必要があります。

rndSelect' :: RandomGen g => [a] -> Int -> g -> ([a], g)
rndSelect' _ 0 gen = ([], gen)
rndSelect' [] _ _ = error "Number of items requested is larger than list"
rndSelect' xs n gen = ((xs !! i) : rest, gen'')
                    where (i, gen') = randomR (0, length xs - 1) gen
                          (rest, gen'') = (rndSelect' (removeAt xs i) (n - 1) gen')

rndSelectIO' :: [a] -> Int -> IO [a]
rndSelectIO' xs n = getStdRandom $ rndSelect' xs n

removeAt :: [a] -> Int -> [a]
removeAt xs n
  | length xs <= n || n < 0 = error "Index out of bounds"
  | otherwise = let (ys, zs) = splitAt n xs
                    in ys ++ (tail zs)

これをロードするとghci、有効な引数に対して正しく機能します。

*Main> rndSelectIO' "asdf" 2 >>= putStrLn 
af

ただし、範囲外のインデックスを使用すると、奇妙なことが起こります。

*Main> rndSelectIO' "asdf" 5 >>= putStrLn
dfas*** Exception: Number of items requested is larger than list
*Main> rndSelectIO' "asdf" 2 >>= putStrLn
*** Exception: Number of items requested is larger than list

ご覧のとおり、次の 2 つの (私にとって) 予期しないことが起こります。

  1. エラーを直接与える代わりに、最初に入力の順列を出力します。
  2. 一度エラーが発生すると、まったく実行されなくなります。

1. は遅延評価と関係があるのではないかと思いますが、なぜ 2. が起こるのかまったくわかりません。何が起きてる?

4

1 に答える 1

13

このgetStdRandom関数は基本的StdGenに、グローバル変数の値を検索し、関数を実行し、新しいシードをグローバル変数に戻し、呼び出し元に結果を返します。

問題の関数がエラーを返した場合、そのエラーはグローバル変数に入れられます。現在、このグローバル変数を使用しようとすると、例外がスローされます。(グローバル変数は悪だと言いました! ;-))

getStdGen自分で手動で呼び出してみてください。現在のランダムシードを出力するか、例外をスローします。例外がスローされた場合...そこに問題があります。

setStdGenを使用してリセットできると思います。

于 2015-06-12T11:48:33.597 に答える