简体   繁体   English

州monad haskell

[英]state monad haskell

I want to write a function for calculating the average using the State Monad in haskell this is the code I wrote as far 我想编写一个函数来计算平均值,使用状态Monad in haskell这是我写的代码

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

I got this error when compile in GHCI, and I stuck there can you help me to understand what is wrong, thank you in advance 我在GHCI编译时遇到这个错误,我坚持在那里你可以帮我理解什么是错的,提前谢谢你

The code you provided gives this error: 您提供的代码会出现此错误:

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

All this means is that the type resulting from the expression ("inferred") disagrees with the type signature ("expected"). 所有这些意味着表达式(“推断”)产生的类型与类型签名(“预期”)不一致。 In this case, getAverage operates in the State monad, so it's the type signature that's incorrect, as it can't evaluate to a non-monadic type. 在这种情况下, getAverageState monad中运行,因此它的类型签名不正确,因为它无法计算为非monadic类型。

Your code has other problems besides that, however, and won't compile even after fixing that particular issue. 但是,除此之外,您的代码还有其他问题,即使在修复该特定问题后也无法编译。 First a few stylistic issues to make it more readable: 首先是一些风格问题,使其更具可读性:

  • getAverage has an unused parameter, which is supposedly a value in the State monad, which doesn't really make sense anyway. getAverage有一个未使用的参数,它应该是State monad中的一个值,无论如何它都没有意义。
  • Using the do notation is usually clearer than using (>>=) and lambdas, especially for something like State . 使用do符号通常比使用(>>=)和lambdas更清晰,特别是对于像State这样的东西。
  • The indentation of the second line is confusing, since the in goes with the let that's inside the lambda. 第二行的缩进是混乱的,因为in去与let那是拉姆达

Making those changes we have this: 做出这些改变我们有这个:

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

...which makes it easier to spot the next error: The second argument of media is a 2-tuple, and s1 is just a single number, but you're trying to use both for the state value. ...这使得更容易发现下一个错误: media的第二个参数是2元组,而s1只是一个数字,但是你试图将两者用于状态值。 Probably what you wanted was to set the state to (x, s1) , but return only x . 可能你想要的是将状态设置为(x, s1) ,但只返回x

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

This compiles just fine, but still needs some tidying: 编译得很好,但仍需要一些整理:

  • media needs to update the entire state value, so rather than get ting and put ting, just use the modify function. media需要更新整个状态值,所以与其get婷和put婷,只需使用modify功能。
  • The return value is the first part of the state value, so just fmap ing fst over get is more straightforward. 返回值是状态值的第一部分,所以只是fmap荷兰国际集团fstget更为直接。

So now we have something like this: 所以现在我们有这样的事情:

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

We can also note that getAverage is kind of doing two different things, and split it into separate functions: 我们还可以注意到, 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

Edit : And since I forgot about the minor detail of actually getting the results back out of the monad, replacing updateAverage with getAverage in Travis Brown's getAverages function will let it work on my code above. 编辑 :因为我忘记了实际将结果从monad中取回的次要细节,在Travis Brown的getAverages函数中用getAverage替换updateAverage将使它适用于我上面的代码。

Note: camccann's answer is better than mine, but mine takes a slightly different approach and gives an example of how to evaluate the state monad, so I'm leaving it here for reference. 注意:camccann的答案比我的好,但我的方法略有不同,并举例说明了如何评估状态monad,所以我将它留在这里作为参考。


We can start trying to figure out the problem by removing the type signature for getAverage and the argument ( c ) that doesn't appear in the function: 我们可以通过删除getAverage的类型签名和函数中没有出现的参数( c )来开始尝试找出问题:

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

This still doesn't compile, because we're trying to put something that doesn't have the right type: s1 is a Double , not a MyState . 这仍然无法编译,因为我们试图put一些没有正确类型的东西: s1Double ,而不是MyState This is easily fixable: 这很容易解决:

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

We could also leave the let pattern unchanged and just say put (x,s1) : I'm doing it this way instead so that our s1 has the same type as s0 . 我们也可以保持let模式不变,只说put (x,s1) :我这样做是为了让我们的s1s0具有相同的类型。

This compiles, so now we can fix the type signature. 这个编译,现在我们可以修复类型签名。 If we ask GHCi for the type, it returns the following: 如果我们向GHCi询问类型,则返回以下内容:

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

Double is an instance of Fractional , and State MyState is an instance of MonadState (Double, Double) , so we can use something very similar to your original type for getAverage : DoubleFractional一个实例, State MyStateMonadState (Double, Double)一个实例,所以我们可以使用与getAverage原始类型非常相似的东西:

getAverage :: Double -> State MyState Double

This function doesn't really "get" the average: it updates it after adding a new value, so let's rename it appropriately: 这个函数并没有真正“获得”平均值:它在添加新值后更新它,所以让我们适当地重命名它:

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

Now we can define a getAverages function that takes a list of Double s, runs them through updateAverage , and returns a list of the intermediate averages at each step: 现在我们可以定义一个getAverages函数,它接受Double的列表,通过updateAverage运行它们,并返回每个步骤的中间平均值列表:

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

This does what we'd expect: 这符合我们的期望:

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

Note that to do anything useful with the State monad you'll always have to use evalState (or the closely related runState and execState ). 请注意,要对State monad执行任何有用的操作,您将始终必须使用evalState (或密切相关的runStateexecState )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM