[英]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. 在这种情况下,
getAverage
在State
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中的一个值,无论如何它都没有意义。 do
notation is usually clearer than using (>>=)
and lambdas, especially for something like State
. do
符号通常比使用(>>=)
和lambdas更清晰,特别是对于像State
这样的东西。 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
功能。 fmap
ing fst
over get
is more straightforward. fmap
荷兰国际集团fst
在get
更为直接。 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
一些没有正确类型的东西: s1
是Double
,而不是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)
:我这样做是为了让我们的s1
与s0
具有相同的类型。
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
: Double
是Fractional
一个实例, State MyState
是MonadState (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
(或密切相关的runState
和execState
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.