解压状态monad是否有合理的方法?

时间:2011-01-08 16:15:17

标签: haskell random state monads

我想要一个像:

这样的功能
unzipState :: (MonadState s m) => m (a, b) -> (m a, m b)

将采用返回元组的(有状态)计算,并返回两个(从属)计算。

当然,困难在于从一个或另一个计算中提取值应更新另一个计算中的状态。

一个有用的(和激励)应用程序是Random monad,表示为

{-# LANGUAGE Rank2types #-}
import qualified System.Random as SR
import Control.Monad.State

type Random a = forall r. (State RandomGen r) => State r a

让我们说你有:

normal :: Random Double
-- implementation skipped

correlateWith :: Double -> Random (Double, Double) -> Random (Double, Double)
correlateWith rho w = do
                        (u, v) <- w
                        return $ (u, p * u + (1 - p * p) * v)

能够写下来是很自然的:

let x = normal
    y = normal
    (u, v) = unzipState $ correlateWith 0.5 $ liftM2 (,) x y
    ... now I am able to perform computation on u and v as correlated random variables

有没有合理的方法呢?我有点挣扎,但没有成功。霍尔格也没有任何帮助。

修改

很好的答案告诉我,我的问题是不明确的。不过,有人可以解释我为什么 python中的以下实现(我认为是正确的,但没有经过多少测试)无法在Haskell中翻译(带有STrefs,闭包和其他东西的魔力我承认我没有掌握;-)):

def unzipState(p):
    flist, glist = [], []
    def f(state):
        if not flist:
            (fvalue, gvalue), newstate = p(state)
            glist.insert(0, gvalue)
            return (fvalue, newstate)
        else:
            fvalue = flist.pop()
            return (fvalue, state)
    def g(state):
        if not glist:
            (fvalue, gvalue), newstate = p(state)
            flist.insert(0, fvalue)
            return (fvalue, newstate)
        else:
            gvalue = glist.pop()
            return (gvalue, state)
    return (f, g)

不是说我说有状态代码可以在Haskell中翻译,但我觉得理解为什么(即使在一个例子中)它无法完成我的理解很多。

edit2

现在很清楚。功能f和g显然不是纯粹的,因为它们的输出不仅取决于状态的值。

再次感谢!

3 个答案:

答案 0 :(得分:5)

不可能构建一个能完成所需要的通用函数unzipState,只是因为你可能无法为其预期效果提供正式的规范。换句话说,假设您已经实现了一些函数unzipState。你怎么知道它是正确的?你必须证明它满足某些定律/方程,但这里的麻烦是首先找到这些定律。


虽然我认为我理解你想做什么,但Random monad也明白了为什么它无法完成。要看到这一点,你必须放弃具体的实现type Random a = ...并考虑

给出的抽象解释
  

v :: Random a表示v是类型为a

的值的概率分布

“绑定”操作(>>=) :: Random a -> (a -> Random b) -> Random b只是从旧概率分布构建新概率分布的一种方法。

现在,这意味着unzipState只返回一对概率分布,可用于构建其他概率分布。关键是虽然do语法看起来非常具有启发性,但您实际上并未采样随机变量,您只需计算概率分布。随机变量可以是相关的,但概率分布不能。


请注意,可以创建与随机变量对应的不同monad RandomVariable a。但是,您必须提前修复样本空间Ω。实施是

type RandomVariable a = Ω -> a



如果你想要随机变量和自动扩大样本空间的能力,你可能需要两个绑定操作

bind1 :: Random Ω a -> (a -> Random Ω b) -> Random Ω b
bind2 :: Random Ω1 a -> (a -> Random Ω2 b) -> Random (Ω1,Ω2) b

和一些依赖类型魔术来处理像(Ω1,(Ω2,Ω3))这样的产品的扩散。

答案 1 :(得分:3)

我并不完全清楚你是如何工作的 - 因为correlateWith对元组进行操作,相关变量有什么意义?说你这样做:

let x = normal
    y = normal
    (u, v) = unzipState $ correlateWith 0.5 $ liftM2 (,) x y
in do u1 <- u
      v1 <- v
      u2 <- u
      u3 <- u
      -- etc...

u1u2u3之间应该存在什么关系?

此外,像这样的“随机变量”可以直接的方式转换为无限懒惰流。以下是什么意思?

let x = normal
    y = normal
    (u, v) = unzipState $ correlateWith 0.5 $ liftM2 (,) x y
in do vs <- generateStream v
      u1 <- u
      if someCondition u1 then u else return u1
      -- etc...

由于从u采样的值的数量会根据u1发生变化,这似乎表明绑定到vs的非monadic值会以某种方式追溯依赖u1同样,这听起来像Haskell避免的远距离的怪异动作。

我猜想你想要完成的事情不能简单地在简单的PRNG状态monad之上进行改装,但可能还有其他选择。

答案 2 :(得分:0)

在Haskell的贝叶斯单子上有很多相关的东西。这是一个参考:http://www.randomhacks.net/articles/2007/02/22/bayes-rule-and-drug-tests

另见本页提供的“纯功能懒惰非确定性编程”:http://www.cs.rutgers.edu/~ccshan/

编辑:我也不明白为什么你不能只给correlateWith以下的类型签名,并直接在do块中运行它而不是试图在let绑定中“推送”随机状态。 correlateWith :: Double -> Random Double -> Random Double -> Random (Double, Double)