みんなこんにちは、
私は今年も Haskell にかなり慣れていません (1990 年代初頭に使用し、2000 年代初頭に使用した後)。Haskell Wiki に示されている IO モナドの例にほぼ直接類似したパターンを使用するコードを作成しようとしています。
type IO a = RealWorld -> (a, RealWorld)
(はい、これは IO の GHC 実装ではなく、単にそれを理解するための手段であることを知っています。) その理由は、私のアプリケーション (ゲーム) で、RealWorld
ここでの 2 つの異なる置換でこれを行う 2 つのパターンがあるためです。 . 1 つはゲームの状態であり、もう 1 つは単なるStdGen
乱数シードです。もちろん、次のようなタイプのペアが 2 つあります。
-- | Easily return a specified value as well as the new random number generator
type ReturnRNG a = (a, StdGen)
-- | Take an RNG and return it and another value.
-- (This is basically like the IO type but with the StdGen instead of RealWorld.)
type WithRNG a = StdGen -> ReturnRNG a
-- | Easily return a specified value as well as the new GameState
type ReturnGS a = (a, GameState)
-- | Use a GameState and return a value with the updated GameState.
-- (This is like IO.)
type WithGS a = GameState -> ReturnGS a
(はい、それらを 2 つのパラメーターを持つ 1 つのペアに抽象化することはできますが、私はそれを理解していません。) もちろん、 myWithGS a
およびWithRNG a
型 (型の同義語) がIO a
上記とまったく同じであることがわかります。
だから、ここに私が今持っている実際の作業コードの簡単な例があります:
-- | Returns a random position for the given size.
randomPos :: (Int, Int) -- ^ The size
-> WithRNG (Int, Int) -- ^ The result (0 up to 1 less than the size) and new RNG seed
randomPos (w, h) r0 = ((x, y), r2)
where
(x, r1) = randomR (0, w - 1) r0
(y, r2) = randomR (0, h - 1) r1
これは、指定された範囲でランダムなペアを作成し、最終的な RNG シードを返します。私のメソッドの大部分はこのようなもので ( WithRNG
orWithGS
を使用)、連鎖状態を使用し、時にはr4
or r6
(またはgs4
など) に至ることさえあります。この例を次のように書きたいと思います...
-- (Not working example)
randomPosSt (w, h) = do
x <- randomR (0, w - 1)
y <- randomR (0, h - 1)
return (x, y)
...まだまったく同じメソッド シグネチャとセマンティクスを持っています。これは、この例を示す前述のチュートリアルに従って可能であるように思われます。
(>>=) :: IO a -> (a -> IO b) -> IO b
(action1 >>= action2) world0 =
let (a, world1) = action1 world0
(b, world2) = action2 a world1
in (b, world2)
ご覧のとおり、これは上で行っていることとほぼ同じです ( let
" " 表記を " " に置き換えればwhere
)。
ただし、型シノニムからモナドを作成することはできません。(私は TypeSynonymInstances を試しましたが、 " instance Monad WithRNG where
" またはパラメーターを使用しても機能しないようです。 a を使用するnewtype
と、役に立たない醜い構文が追加されるようです。) State Monad を十分に理解することができませんでした。どちらかを使用して同等のメソッドを作成します。しかし、私が成功したとしても、State Monad の実装は醜い " get
" と " put
" (および " runState
" など) を使用しているように見え、コードが読みにくくなるだけでなく、読みにくくなるでしょう。
-- THIS DOES NOT WORK
-- | Make a State Monad with random number generator - like WithRNG above
type RandomState = State StdGen
-- | Returns a random position for the given size.
randomPosSt :: (Int, Int) -- ^ The size
-> RandomState (Int, Int) -- ^ The result (0 up to 1 less than the size) and new RNG seed
このすべての後、私は何か間違ったことをしている、何かを誤解している、または単にやりたいことができないという結論に達しました. 「まあ、コードを変更して状態を自動的に処理するように変更する必要はありません。問題なく動作するので」とあきらめようとしていました。ここで聞いてください(私のデビューデラーキング)。私はよりエレガントなソリューションを好むでしょう。
私はまた、よりエレガントなソリューションが、私が「無料で」使用するこの機能を提供してくれると考えています。
-- | Maps the specified method, which must take a RNG as the last parameter,
-- over all the elements of the list, propagating the RNG and returning it.
-- TODO: Implement this without using recursion? Using a fold?
mapRandom :: (a -> WithRNG b) -- ^ The function to map (that takes a RNG)
-> [a] -- ^ The input list
-> WithRNG [b] -- ^ The RNG to return
mapRandom func [] r0 = ([], r0)
mapRandom func (x:xs) r0 = (mapped : rest, r2)
where
(mapped, r1) = func x r0
(rest, r2) = mapRandom func xs r1
考え、提案、参照、そしてあなたの時間をありがとう!