2

#文字列内の各出現を別の乱数に置き換える関数を提供したいと考えています。非純粋な言語では、それは些細なことです。しかし、純粋な言語でどのように設計する必要がありますか? unsafePerformIOはハックのように見え、適切な設計ではないため、使用したくありません。

この関数は、パラメーターの 1 つとしてランダム ジェネレーターを必要としますか? もしそうなら、そのジェネレーターは呼び出しのスタック全体を通過する必要がありますか? 他の可能なアプローチはありますか?Stateここでモナドを使うべきですか?実行可能なアプローチを示すおもちゃの例をいただければ幸いです...

4

2 に答える 2

2

実際には、状態モナドのバリアントを使用して、乱数ジェネレーターを舞台裏で渡します。RandタイプインはこれにControl.Monad.Random役立ちます。API は少し混乱しますが、それは機能する必要があるというよりも、使用するランダム ジェネレーターの種類が多態的であるためです。ただし、この余分な足場は便利です。既存のコードをさまざまなランダム ジェネレーターで簡単に再利用できるため、さまざまなアルゴリズムをテストできるだけでなく、ジェネレーターが決定論的 (テストに適している) か、外部データをシードする ( IO)。

動作中の簡単な例を次に示しRandます。RandomGen g =>型シグネチャのは、任意の型の乱数発生器を使用できることを示しています。明示的に として注釈nを付ける必要があります。そうしないと、生成して文字列に変換できる数値型でなければならないIntことを GHC しか認識していないためです。Double

randomReplace :: RandomGen g => String -> Rand g String
randomReplace = foldM go ""
  where go str '#' = do
          n :: Int <- getRandomR (0, 10)
          return (str ++ show n)
        go str chr = return $ str ++ [chr]

これを実行するには、どこかから乱数発生器を取得して に渡す必要がありevalRandます。これを行う最も簡単な方法は、で実行できるグローバル システム ジェネレーターを取得することですIO

main :: IO ()
main = do gen <- getStdGen
          print $ evalRand (randomReplace "ab#c#") gen

これは非常に一般的なパターンであり、ライブラリevalRandIOがそれを行う関数を提供します:

main :: IO ()
main = do res <- evalRandIO $ randomReplace "ab#c#"
          print res

最終的に、コードは乱数発生器を使用してそれを渡すことについてもう少し明示的ですが、それでもかなり簡単に理解できます。より複雑なコードについてはRandT、 を使用することもできます。これにより、他のモナド ( などIO) を拡張してランダムな値を生成することができ、すべての配管とセットアップをコードの一部に任せることができます。

于 2015-03-05T22:04:31.907 に答える
0

それは単なるモナドマッピングです

import Control.Applicative
import Control.Monad.Random
import Data.Char

randomReplace :: RandomGen g => String -> Rand g String
randomReplace = mapM f where
    f '#' = intToDigit <$> getRandomR (0, 10)
    f  c  = return c

main = evalRandIO (randomReplace "#abc#def#") >>= print
于 2015-03-05T22:44:29.523 に答える