州monad haskell

时间:2010-07-30 17:06:31

标签: haskell monads state-monad

我想编写一个函数来计算使用haskell中的State Monad的平均值 这是我到目前为止编写的代码

import Control.Monad.State
type MyState = (Double,Double)
media s (a,n)= ((a*n+s)/(n+1),n+1)

getAverage:: Double ->State MyState  s1-> Double
getAverage s c=get >>= \s0 -> let (x,s1) =media s s0
            in put s1 >> return x

我在GHCI编译时遇到了这个错误,我就陷入了困境 你可以帮我理解什么是错的,提前谢谢你

2 个答案:

答案 0 :(得分:6)

您提供的代码会出现此错误:

Couldn't match expected type `Double'
       against inferred type `m Double'
In the expression:
      get >>= \ s0 -> let (x, s1) = ... in put s1 >> return x
In the definition of `getAverage':
    getAverage s c = get >>= \ s0 -> let ... in put s1 >> return x

所有这些意味着表达式(“推断”)产生的类型与类型签名(“预期”)不一致。在这种情况下,getAverageState monad中运行,因此它的类型签名不正确,因为它无法评估为非monadic类型。

除此之外,您的代码还有其他问题,即使在修复该特定问题后也无法编译。首先是一些风格问题,使其更具可读性:

  • getAverage有一个未使用的参数,据说是State monad中的一个值,无论如何都没有意义。
  • 使用do表示法通常比使用(>>=)和lambdas更清晰,特别是State之类的内容。
  • 第二行的缩进令人困惑,因为inlet内的内部 lambda一起使用。

进行这些更改我们有:

getAverage s = do
    s0 <- get
    let (x, s1) = media s s0
    put s1 
    return x

...这样可以更容易地发现下一个错误:media的第二个参数是2元组,而s1只是一个数字,但您正在尝试使用两者都是为了国家价值。可能你想要的是将状态设置为(x, s1),但只返回x

getAverage s = do
    s0 <- get
    let (x,s1) = media s s0
    put (x,s1)
    return x

这个编译很好,但仍需要一些整理:

  • media需要更新整个州的值,而不是getput,只需使用modify功能。
  • 返回值是州值的第一部分,因此仅fmap fst get超过media :: Double -> MyState -> MyState media s (a, n) = ((a * n + s) / (n + 1), n + 1) getAverage:: Double -> State MyState Double getAverage s = do modify (media s) fmap fst get 更为直接。

所以现在我们有这样的事情:

getAverage

我们还可以注意到updateAverage:: Double -> State MyState () updateAverage s = modify (media s) currentAverage :: State MyState Double currentAverage = fmap fst get getAverage:: Double -> State MyState Double getAverage s = updateAverage s >> currentAverage 有两种不同的东西,并将它分成不同的函数:

updateAverage

修改:因为我忘记了实际将结果从monad中取回的细节,在Travis Brown的{{1}中将getAverage替换为getAverages函数将使它适用于我上面的代码。

答案 1 :(得分:3)

注意:camccann的答案比我的好,但是我的方法略有不同,并给出了如何评估状态monad的示例,所以我将它留在这里作为参考。


我们可以通过删除getAverage的类型签名和函数中没有出现的参数(c)来开始尝试解决问题:

getAverage s=get >>= \s0 -> let (x,s1) =media s s0
            in put s1 >> return x

这仍然无法编译,因为我们正在尝试put某些类型不正确的内容:s1Double,而不是MyState }。这很容易解决:

getAverage s=get >>= \s0 -> let s1@(x,_) =media s s0
            in put s1 >> return x

我们也可以保持let模式不变,只说put (x,s1):我这样做是为了让我们的s1s0具有相同的类型

这个编译,现在我们可以修复类型签名。如果我们向GHCi询问类型,则返回以下内容:

getAverage :: (Fractional t, MonadState (t, t) m) => t -> m t

DoubleFractional的一个实例,State MyStateMonadState (Double, Double)的一个实例,因此我们可以使用与getAverage的原始类型非常相似的内容}:

getAverage :: Double -> State MyState Double

这个函数并没有真正“获得”平均值:它在添加新值后更新它,所以让我们适当地重命名它:

updateAverage :: Double -> State MyState Double
updateAverage s=get >>= \s0 -> let s1@(x,_) =media s s0
            in put s1 >> return x

现在我们可以定义一个getAverages函数,它接受Double的列表,通过updateAverage运行它们,并返回每个步骤的中间平均值列表:

getAverages :: [Double] -> [Double]
getAverages ss = evalState (mapM updateAverage ss) (0, 0)

这就是我们所期望的:

*Main> getAverages [1..10]
[1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5]

请注意,要对State monad执行任何有用的操作,您必须始终使用evalState(或密切相关的runStateexecState)。