允许组合有状态函数的最简单的 Haskell 库是什么?
我们可以使用 State
monad 来计算股票的指数加权移动平均线,如下所示:
import Control.Monad.State.Lazy
import Data.Functor.Identity
type StockPrice = Double
type EWMAState = Double
type EWMAResult = Double
computeEWMA :: Double -> StockPrice -> State EWMAState EWMAResult
computeEWMA α price = do oldEWMA <- get
let newEWMA = α * oldEWMA + (1.0 - α) * price
put newEWMA
return newEWMA
但是,编写调用其他有状态函数的函数很复杂。 例如,要查找股票短期平均值与其长期平均值交叉的所有数据点,我们可以这样写:
computeShortTermEWMA = computeEWMA 0.2
computeLongTermEWMA = computeEWMA 0.8
type CrossingState = Bool
type GoldenCrossState = (CrossingState, EWMAState, EWMAState)
checkIfGoldenCross :: StockPrice -> State GoldenCrossState String
checkIfGoldenCross price = do (oldCrossingState, oldShortState, oldLongState) <- get
let (shortEWMA, newShortState) = runState (computeShortTermEWMA price) oldShortState
let (longEWMA, newLongState) = runState (computeLongTermEWMA price) oldLongState
let newCrossingState = (shortEWMA < longEWMA)
put (newCrossingState, newShortState, newLongState)
return (if newCrossingState == oldCrossingState then
"no cross"
else
"golden cross!")
由于 checkIfGoldenCross 调用computeShortTermEWMA 和computeLongTermEWMA,我们必须手动包装/解开它们的状态。
有没有更优雅的方式?
最佳答案
如果我正确理解您的代码,则您不会在对 computeShortTermEWMA
和 computeLongTermEWMA
的调用之间共享状态。它们只是两个完全独立的函数,恰好在内部使用状态。在这种情况下,优雅的做法是将 runState
封装在 computeShortTermEWMA
和 computeLongTermEWMA
的定义中,因为它们是独立的包含的实体:
computeShortTermEWMA start price = runState (computeEWMA 0.2 price) start
这一切只是让调用站点变得更整洁;我刚刚将 runState
移至定义中。这标志着该状态是计算 EWMA 的本地实现细节,这才是它的真正含义。 GoldenCrossState
与 EWMAState
是不同的类型,这一点强调了这一点。
换句话说,您并没有真正编写有状态函数;而是在编写有状态函数。相反,您正在编写恰好在内部使用状态的函数。您可以隐藏该详细信息。
更一般地说,我根本不明白你使用状态来做什么。我想您会用它来迭代股票价格,维护 EWMA。但是,我认为这不一定是最好的方法。相反,我会考虑使用扫描之类的方法在股票价格列表上编写 EWMA 函数。这应该会使您的其他分析函数更容易实现,因为它们也只是列表函数。 (将来,如果您需要处理 IO,您可以随时切换到 Pipes 之类的东西,它提供的界面非常类似于列表。)
关于haskell - 在 Haskell 中编写有状态函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24833714/