我想要一个像:
这样的功能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显然不是纯粹的,因为它们的输出不仅取决于状态的值。
再次感谢!
答案 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...
u1
,u2
和u3
之间应该存在什么关系?
此外,像这样的“随机变量”可以直接的方式转换为无限懒惰流。以下是什么意思?
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)