2017-10-18 14 views
3

私はStateと再帰を一緒に使用するいくつかのコードを作成しています。おそらくこの部分は実際には必要ではありません(それは相対的に初心者であっても、すでに私にとっては不器用な感じです)。しかし、おそらくそうする部分があります。MonadRandom、State and Monadトランスフォーマー

私の初期の純粋な実装は完全です決定論的(入札の選択は、単に機能validBidsが提供する最初のオプションです):

bidOnRound :: (DealerRules d) => d -> NumCards -> State ([Player], PlayerBids)() 
bidOnRound dealerRules cardsThisRound = do 
    (players, bidsSoFar) <- get 
    unless (List.null players) $ do 
    let options = validBids dealerRules cardsThisRound bidsSoFar 
    let newBid = List.head $ Set.toList options 
    let p : ps = players 
    put (ps, bidsSoFar ++ [(p, newBid)]) 
    bidOnRound dealerRules cardsThisRound 

そして、私からそれを呼び出す:今、私は私がにランダム性を持参する必要があることを承知している

playGame :: (DealerRules d, ScorerRules s) => d -> s -> StateT Results IO() 
    ... 
let (_, bidResults) = execState (bidOnRound dealerRules cardsThisRound) (NonEmpty.toList players, []) 

これと、コード。 IOどこにでもゴミを捨てることも、手作業で丸ごとランダムに種まちを渡すことも望ましくありません。MonadRandomまたは何かを使用する必要があります。私が使っている図書館はそれを有効に使う。これは賢明な選択ですか?

ここに私が試したものです:

bidOnRound :: (DealerRules d, RandomGen g) => d -> NumCards -> RandT g (State ([Player], PlayerBids))() 
bidOnRound dealerRules cardsThisRound = do 
    (players, bidsSoFar) <- get 
    unless (List.null players) $ do 
    let options = Set.toList $ validBids dealerRules cardsThisRound bidsSoFar 
    rnd <- getRandomR (0 :: Int, len options - 1) 
    let newBid = options List.!! rnd 
    let p : ps = players 
    put (ps, bidsSoFar ++ [(p, newBid)]) 
    bidOnRound dealerRules cardsThisRound 

が、私はすでに不快だ、プラス例えば、これを呼び出す方法を考え出すことはできませんexecStateと組み合わせてevalRandを使用してなど、私は他の人対MonadRandomRandGenmtlで読むより、少ない必ず私がやっているのだ...私はきれいにランダム性を兼ね備えなければならない方法

Stateとどのように行います私はこれらを正しく呼んでいますか?

ありがとうございます!

EDIT:参考のためfull current source on Github

答えて

7

あなたの手助けをしてくれる例があります。あなたが完全に動作するコードスニペットを投稿していなかったので、私は自分の業務の多くを交換し、モナドを評価できる方法を紹介します:

import Control.Monad.Trans.State 
import Control.Monad.Random 
import System.Random.TF 

bidOnRound :: (RandomGen g) => Int -> RandT g (State ([Int], Int))() 
bidOnRound i = 
do rand <- getRandomR (10,20) 
    s <- lift $ get 
    lift $ put ([], i + rand + snd s) 

main :: IO() 
main = 
do g <- newTFGen 
    print $ flip execState ([],1000) $ evalRandT (bidOnRound 100) g 

ここで注意すべきことは、あなたが外側のモナド「アンラップ」であります最初。あなたがRandT (StateT Reader ...) ...を持っているなら、RandTを(evalRandTまたはそれに類するものを経由して)実行し、次に状態を読んでください。第二に、内側モナドの操作を使用するには、外側モナドからliftを入力する必要があります。これは不器用に思えるかもしれません。それはひどく不器用なのでです。

私が知っている最良の開発者 - コードを見て仕事を楽しむ人 - はモナド操作を抽出し、すべてのプリミティブをAPIに提供するので、モナドの構造について考える必要はありません。私が書いているロジックの構造について考えている。この場合

(私は韻や理由、任意のアプリケーションドメインなし上記を書いたので、それは少し不自然になります)あなたが書くことができる:

type MyMonad a = RandT TFGen (State ([Int],Int)) a 

runMyMonad :: MyMonad() -> IO Int 
runMyMonad f = 
do g <- newTFGen 
    pure $ snd $ flip execState ([],1000) $ evalRandT f g 

モナドでは、単純なエイリアスと実行動作として定義され基本的な機能は簡単です:通常、実際のアプリケーションを作るのマイナーな部分である道のうちその脚仕事で

flipCoin :: MyMonad Int 
flipCoin = getRandomR (10,20) 

getBaseValue :: MyMonad Int 
getBaseValue = snd <$> lift get 

setBaseValue :: Int -> MyMonad() 
setBaseValue v = lift $ state $ \s -> ((),(fst s, v)) 

、ドメイン固有のロジックを書くことが容易に且つ確実に読みやすいです。

bidOnRound2 :: Int -> MyMonad() 
bidOnRound2 i = 
do rand <- flipCoin 
    old <- getBaseValue 
    setBaseValue (i + rand + old) 

main2 :: IO() 
main2 = print =<< runMyMonad (bidOnRound2 100) 
+0

これは素晴らしいです、ありがとう。私は今夜​​それを正しく処理します。私は現在のGithubのソースに実際にリンクしていたはずです...今すぐやります。 – declension

関連する問題