【问题标题】:How to store ST monad thing?如何存储 ST monad 的东西?
【发布时间】:2015-06-04 06:44:48
【问题描述】:

我想在ST monad 之外处理/存储随机生成器(Gen (ST {..}),但我找不到怎么做。

背景

我正在做一些大量使用随机的模拟。 通过分析,我知道生成随机数需要超过 50% 的处理时间。

为了生成随机数,我使用mwc-randomSFMT
由于速度问题,我主要使用SFMT
但是,与SFMTmwc-random 相比,我需要更丰富的接口(如normalbernoulli、..)。

在基准测试和阅读代码之后,我了解到mwc-randomST monad 上使用时不会比SFMT 慢。
SFMTIOMWCSTMWCIOSFMTST
所以,我想在ST monad 上制作和处理MWC 随机生成器。 但是,我不能像其他 ST 一样从 ST monad 中取出这个生成器(例如 STRef)。

问题

有没有办法在ST monad 之外安全地处理/存储这个随机生成器?

我尝试使用 STRef 或其他东西从许多包/代码中学习,但我无法弄清楚。

示例

我在模拟中这样使用随机生成器。

import qualified System.Random.MWC as MWC
import GHC.Prim
import Control.Monad

data World = World { randomGen :: MWC.Gen RealWorld }

initWorld = do gen <- MWC.create
               return $ World gen

something gen = do num <- MWC.uniformR (1,100) gen :: IO Int
                   print num

main = do world <- initWorld
          replicateM_ 100 $ something (randomGen world)

但是,这段代码不起作用。

import qualified System.Random.MWC as MWC
import Control.Monad
import Control.Monad.Primitive
import Control.Monad.ST

data World s = World { randomGen :: MWC.Gen (PrimState (ST s))}

initWorld :: ST s (World s)
initWorld = do gen <- MWC.create
               return $ World gen

something gen = do
    let num :: Int
        num = runST $ do num <- MWC.uniformR (1,100) gen
                         return num
    print num

main = do let world = runST initWorld
          replicateM_ 100 $ something (randomGen world)

我想重写此代码以使用something。 我需要定义/重写数据结构还是做其他事情? 还有更聪明的方法吗?

积分:

  1. 我需要处理一个随机生成器(如Gen (PrimState (ST s)))来重现结果。
    所以,我不想制作临时随机生成器。
  2. 我不想保存/恢复种子。它的开销太大。 (保存/恢复种子比生成一个随机数需要 x12~15 倍)
    它比在 IO monad 上使用要慢,所以我不需要在 ST monad 上做。
  3. 我不想使用 unsafe* 函数。

【问题讨论】:

  • 您不能同时拥有 2 和 3。 saverestore 是安全的,因为它们复制了底层向量。不复制任何东西都不会阻止改变一个所谓的不可变值。难道你不能通过在单个 ST 范围或内部 IO 中完成所有(或大部分)工作来避免保存和恢复吗?
  • 另外,这可能是unsafeInterleaveST 是“净化”增量操作的好选择。

标签: haskell monads st


【解决方案1】:

您不应该尝试在 ST monad 之外操作生成器。由于runST 的类型,尝试使用存在于状态线程“外部”“内部”的东西是没有意义的。想象一下,您有以下类型的函数(这是您要编写的函数):

something :: MWC.Gen s -> Int
something gen = runST ... 

为了生成随机数,必须对Gen 中的数据进行一些有状态的计算。这些计算将在什么时候完成?如果有的话,他们会做多少次?最重要的是 - something 如何生成随机数 - 毕竟它是一个纯函数,因此它必须为相同的输入返回相同的值。

相反,您应该将状态线程化,并在最后调用runST

something :: MWC.Gen s -> ST s Int 
something = MWC.uniformR (1,100) 

main = mapM_ print $ runST $ do 
  w0 <- initWorld 
  replicateM 100 (something $ randomGen w0)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多