【问题标题】:MonadFix instance for Rand monadRand monad 的 MonadFix 实例
【发布时间】:2011-07-18 22:16:37
【问题描述】:

我想用来自System.Random.MWC.Monad 的 Rand monad 生成无限的数字流。如果这个 monad 有一个 MonadFix 实例,或者像这样的实例:

instance (PrimMonad m) => MonadFix m where
     ...

然后可以写:

runWithSystemRandom (mfix (\ xs -> uniform >>= \x -> return (x:xs)))

虽然没有。

我正在经历MonadFix docs,但我没有看到实现此实例的明显方法。

【问题讨论】:

  • 你为什么不用mapM (repeat uniform)?
  • 我认为你的意思是sequence (repeat uniform)。好吧,问题是像runWithSystemRandom (asRandST $ sequence (repeat uniform)) 这样的代码在输出中并不懒惰,即在完成计算之前永远不会产生单个数字,这是无限的。
  • 是的,你是对的。而这确实是个问题。

标签: haskell random monads monadfix


【解决方案1】:

(这更适合作为对 Heatsink 答案的评论,但它有点太长了。)

MonadFix 实例必须遵守several laws。其中之一是左缩/变细:

mfix (\x -> a >>= \y -> f x y)  =  a >>= \y -> mfix (\x -> f x y)

这条定律允许将你的表达式改写为

mfix (\xs -> uniform >>= \x -> return (x:xs))
= uniform >>= \x -> mfix (\xs -> return (x:xs))
= uniform >>= \x -> mfix (return . (x :))

使用另一个定律,纯度 mfix (return . h) = return (fix h),我们可以进一步简化为

= uniform >>= \x -> return (fix (x :))

并使用标准单子定律并将fix (x :) 重写为repeat x

= liftM (\x -> fix (x :)) uniform
= liftM repeat uniform

因此,结果确实是一次调用uniform,然后只是无限期地重复单个值。

【讨论】:

    【解决方案2】:

    一个问题:您希望如何生成初始种子?

    问题在于 MWS 建立在“原始”包之上,该包仅抽象 IO 和严格(Control.Monad.ST.ST s)。它也不抽象惰性(Control.Monad.ST.Lazy.ST s)。

    也许可以为“原始”创建实例以覆盖惰性 ST,然后 MWS 可能是惰性的。

    更新:我可以使用 Control.Monad.ST.Lazy 通过使用 strictToLazyST 来完成这项工作:

    module Main where
    
    import Control.Monad(replicateM)
    import qualified Control.Monad.ST as S
    import qualified Control.Monad.ST.Lazy as L
    import qualified System.Random.MWC as A
    
    foo :: Int -> L.ST s [Int]
    foo i = do rest <- foo $! succ i
               return (i:rest)
    
    splam :: A.Gen s -> S.ST s Int
    splam = A.uniformR (0,100)
    
    getS :: Int -> S.ST s [Int]
    getS n = do gen <- A.create
                replicateM n (splam gen)
    
    getL :: Int -> L.ST s [Int]
    getL n = do gen <- createLazy
                replicateM n (L.strictToLazyST (splam gen))
    
    createLazy :: L.ST s (A.Gen s)
    createLazy = L.strictToLazyST A.create
    
    makeLots :: A.Gen s -> L.ST s [Int]
    makeLots gen = do x <- L.strictToLazyST (A.uniformR (0,100) gen)
                      rest <- makeLots gen
                      return (x:rest)
    
    main = do
      print (S.runST (getS 8))
      print (L.runST (getL 8))
      let inf = L.runST (foo 0) :: [Int]
      print (take 10 inf)
      let inf3 = L.runST (createLazy >>= makeLots) :: [Int]
      print (take 10 inf3)
    

    【讨论】:

    • 初始种子将从外部提供,即 /dev/random 或 time(0) 或任何东西。
    • 因此修改 inf3:将种子传递给 createLazy,然后在您的 makeLots 变体上的 L.runST 将返回由 MWC 生成的随机值的 pure_infinite_lazy_list。不需要导入或使用 Rand monad。
    【解决方案3】:

    您可以编写一个 MonadFix 实例。但是,代码不会生成无限的不同随机数流。 mfix 的参数是一个只调用一次uniform 的函数。当代码运行时,它将只调用一次uniform,并创建一个包含结果的无限列表。

    你可以试试等效的 IO 代码看看会发生什么:

    import System.Random
    import Control.Monad.Fix
    main = print . take 10 =<< mfix (\xs -> randomIO >>= (\x -> return (x : xs :: [Int])))
    

    您似乎想使用有状态的随机数生成器,并且想要运行生成器并懒惰地收集其结果。如果不仔细使用unsafePerformIO,这是不可能的。除非您需要快速生成许多随机数,否则您可以使用纯 RNG 函数,例如 randomRs。

    【讨论】:

    • 运行生成器并延迟收集结果可以使用ST monad 或最坏情况下的unsafeInterleaveIO 来完成,而不是unsafePerformIO。
    • @John 一个 ST 随机数生成器在需要其输出时急切地计算其所有输出。当输出是一个无限列表时,Eager evaluation 就不能很好地工作了。
    猜你喜欢
    • 2014-11-06
    • 2016-11-05
    • 2015-05-23
    • 2012-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-22
    相关资源
    最近更新 更多