3

只是学习如何更直观地掌握单子和转换器;很多看起来很明显的事情对我来说仍然有点棘手哈哈。

所以我有一个存在于Randmonad 中的计算,但其中,还有另一个“子计算”(或多个)存在于一个STmonad(或Statemonad,对于所有重要的事情......ST仅用于性能但我认为State有效)在这种情况下也是如此)。

整个计算不需要在STmonad 内部......并且这个子计算将以不同的起始状态多次调用,所以我不想将整个事情强制转换为ST(除非这是惯用的方式) .

没有随机性,结构看起来像这样:

main = print mainComp

mainComp :: Int
mainComp = otherComp + (subComp 1) + (subComp 2)

subComp :: Int -> Int
subComp n = runST $ do
  -- generate state based on n
  -- ...
  replicateM_ 100 mutateState
  -- ...
  -- eventually returns an ST s Int

mutateState :: ST s ()
mutateState = -- ...

基本上一切都很好,并且在 和 中有完全的引用透明mainCompsubComp

这就是我迄今为止使用的方式Rand——

main = (evalRandIO mainComp) >>= print

mainComp :: (RandomGen g) => Rand g Int
mainComp = do
  subResultA <- subComp 1
  subResultB <- subComp 2
  return $ otherComp + subResultA + subResultB

subComp :: (RandomGen g) => Int -> Rand g Int
subComp = return $ runST $ do           -- is this ok to just throw in return?
  -- generate state based on n
  -- ...
  replicateM_ 100 mutateState
  -- ...
  -- eventually returns an ST s Int (??)

mutateState :: ??
mutateState = ??

mutateState如果我想在其中使用随机种子和Rand单子,应该是什么类型?我想我可能想要使用的返回类型RandT g (ST s) (),但我如何使它适合 in 中预期的runST类型subComp

4

1 回答 1

1

使用 monad 转换器,您可以按照添加它们的相反顺序“剥离”层。所以,如果你有一些 type RandT g (ST s) (),你首先使用evalRandTor消除 RandT runRandT,然后才调用runST

这是一个结合RandTand的简单示例ST

import Data.STRef
import System.Random

import Control.Monad
import Control.Monad.Trans
import Control.Monad.ST
import Control.Monad.Random
import Control.Monad.Random.Class

stNrand :: RandT StdGen (ST s) Int
stNrand = do
    ref <- lift $ newSTRef 0
    i <- getRandomR (0,10)
    lift $ writeSTRef ref i
    lift $ readSTRef ref

main :: IO ()
main = putStrLn . show $ runST $ evalRandT stNrand (mkStdGen 77)

编辑:这是一个扩展版本,现在有一个函数runSTBelowRand可以让你在计算中嵌入RandT StdGen (ST s) a计算Rand StdGen a。该函数使用getSplit来拆分全局计算的种子,并将新种子提供给子计算。

{-# LANGUAGE RankNTypes #-}

import Data.STRef
import System.Random

import Control.Monad
import Control.Monad.Trans
import Control.Monad.ST
import Control.Monad.Random
import Control.Monad.Random.Class

stNrand :: RandT StdGen (ST s) Int
stNrand = do
    ref <- lift $ newSTRef 0
    i <- getRandomR (0,10)
    lift $ writeSTRef ref i
    lift $ readSTRef ref

runSTBelowRand :: (forall s. RandT StdGen (ST s) a) -> Rand StdGen a
runSTBelowRand r = do
    splittedSeed <- getSplit
    return $ runST $ evalRandT r splittedSeed

globalRand :: Rand StdGen (Int,Int)
globalRand = do
    i1 <- runSTBelowRand stNrand
    -- possibly non-ST stuff here
    i2 <- runSTBelowRand stNrand
    return (i1,i2)

main :: IO ()
main = putStrLn . show $ evalRand globalRand (mkStdGen 77)
于 2013-07-07T22:22:49.517 回答