在State monad上实现递归关系(在Haskell或Scala中)

exp*_*ite 5 monads recurrence haskell state-monad moving-average

我正在http://www.thalesians.com/archive/public/academic/finance/papers/Zumbach_2000.pdf中对运营商进行新的实施 编辑:此处的解释更清楚:https : //www.olseninvest.com/客户/pdf/paper/001207-emaOfEma.pdf

简而言之,它是一堆基于指数移动平均值的递归关系的很酷的时间序列运算符,其中ema()运算符的每个应用都采用新值和ema的先前结果。我似乎无法在此堆栈交换上进行乳胶操作,但是无论如何我现在的问题是软件问题。

我在Scala中通过在创建EMA函数的thunk中隐藏了一个变量来实现了这一点。所有这些都可以,但是非常棘手,因为再次调用ema(5)然后再次调用ema(5)自然会导致不同的结果。我想使用State Monads重做所有这些操作,但是我很快就迷失了自己。

例如,我在Haskell中具有以下简化的EMA State monad:

import Control.Monad.State

type EMAState = Double
type Tau = Double

ema :: Tau -> Double -> State EMAState Double
ema tau x = state $ \y ->
  let alpha = 1 / tau
      mu = exp(-alpha)
      mu' = 1 - mu
      y' = (mu * y) + (mu' * x)
  in (y', y')
Run Code Online (Sandbox Code Playgroud)

我可以在GHCI中轻松测试:

*Main Control.Monad.State> runState (ema 5 10) 0
(1.8126924692201818,1.8126924692201818)
Run Code Online (Sandbox Code Playgroud)

将输入10应用于初始化为0的5周期EMA。这很好,使用forM可以应用多个输入值,等等。现在,下一步是实现“迭代EMA”,即应用的EMA自己N次。

iEMA[n](x) = EMA(iEMA[n-1](x))
Run Code Online (Sandbox Code Playgroud)

这些中间EMA均需要具有自己的状态(即先前的结果),才能正确计算迭代EMA的向量。所以,我正在寻找的是这样的东西(我认为):

iema :: Int -> Tau -> Double -> State [EMAState] [Double]
Run Code Online (Sandbox Code Playgroud)

本质上是EMA的菊花链:

iEMA[3](x) = EMA(EMA(EMA(x,s1),s2),s3) = (x, [s1,s2,s3]) -> ([y1,y2,y3], [s1',s2',s3'])
Run Code Online (Sandbox Code Playgroud)

如果我只关心第三个迭代EMA ...

... -> (y3, [s1', s2', s3'])
Run Code Online (Sandbox Code Playgroud)

本文从此继续发展,在迭代的EMA及其平均数等基础上创建越来越复杂的运算符,因此我希望能够在功能上和纯粹上构成这些有状态运算符,从而构建更加复杂的状态,但输入和输出仍然非常简单。

我真的感觉这就是函数式编程擅长的领域,但是我还没有专门知识来了解如何以正确的方式组合这些State monad。有人可以通过这些迭代的递归运算符为我指明正确的方向吗?

编辑:

几个有用的人建议对输入数据重复使用相同的ema运算符,但这还不够。每个ema运算符都需要保持自己的先前值。这是一个例子:

tau 5               
mu  0.818730753             
muprime 0.181269247             
        ema1    ema2    ema3     
    x   0       0       0       <- States_0
    1   0.1812  0.03285 0.00595 <- States_1
    5   1.0547  0.21809 0.04441 <- States_2
Run Code Online (Sandbox Code Playgroud)

x列是原始输入,ema1使用其左侧作为输入,使用up1作为重复/状态。ema2的左边用于输入(不是x!),它用于状态。这是ema(ema(x))。同上ema3 = ema(ema(ema(ema(x))))。我想做的事,我认为必须是可行的,给它一个ema状态monad,组成ema3状态monad,甚至更好的是[ema]状态monad,每个随后的ema都对前一个输出进行操作。

Eri*_*ikR 1

更新了答案...

定义:

combine :: [ a -> State s a ] -> a -> State [s] a
combine fs a = state $ \ys ->
  let zs = zipWith (\f y a -> runState (f a) y) fs ys
      pairs = chain a zs
      as' = map fst pairs
      a' = last as'         -- we are only returning one result in this case
      ys' = map snd pairs
  in (a', ys')

chain :: a -> [ a -> (a,s) ] -> [ (a,s) ]
chain a [] = []
chain a (f:fs) = let (a',s) = f a
                 in (a',s) : chain a' fs

ema3 t = combine $ replicate 3 (ema t)

ghci> runState (ema3 5 1) [0,0,0]
(5.956242778945897e-3,[0.18126924692201818,3.2858539879675595e-2,5.956242778945897e-3])

ghci> runState (do ema3 5 1; ema3 5 5) [0,0,0]
(4.441089130249448e-2,[1.0547569416524334,0.21809729359983737,4.441089130249448e-2])
Run Code Online (Sandbox Code Playgroud)

可以combine轻松修改以返回所有结果 - 只需 returnas'而不是a'

原答案:

 combine :: (a -> State s b) -> (b -> State t c) -> (a -> State (s,t) c)
 combine f g a = state $ \(s,t) ->
   let (b,s') = runState (f a) s
       (c,t') = runState (g b) t
   in (c,(s',t'))
Run Code Online (Sandbox Code Playgroud)

然后:

ema3 tau = ema tau `combine` ema tau `combine` ema tau
Run Code Online (Sandbox Code Playgroud)

并且em3有类型:

ema3 :: Tau -> Double -> State ((EMAState, EMAState), EMAState) Double
Run Code Online (Sandbox Code Playgroud)

例如:

ghci> runState (ema3 5 1) ((0,0),0)
(5.956242778945897e-3,((0.18126924692201818,3.2858539879675595e-2),5.956242778945897e-3))
Run Code Online (Sandbox Code Playgroud)

请注意, 的状态类型ema3不是((Double,Double),Double)三元组或列表。

在您的示例中,您(ema3 5)首先使用输入运行x = 1,然后使用x = 5 具有初始状态的输入运行((0,0),0)

ghci> runState (do ema3 5 1; ema3 5 5) ((0,0),0)
(4.441089130249448e-2,((1.0547569416524334,0.21809729359983737),4.441089130249448e-2))
Run Code Online (Sandbox Code Playgroud)

这就是表中的第二行。