蒙纳德·哈斯克尔州 [英] state monad haskell
问题描述
我想编写一个用于使用Haskell中的State Monad计算平均值的函数 这是我到目前为止编写的代码
I want to write a function for calculating the average using the State Monad in haskell this is the code I wrote as far
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中编译时出现此错误,并且卡在了那里 您能帮助我了解问题出在哪吗?
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
推荐答案
您提供的代码会出现此错误:
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
这意味着从表达式(推断")得到的类型与类型签名(期望")不同.在这种情况下,getAverage
在State
monad中运行,因此类型签名是错误的,因为它不能求值为非monadic类型.
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.
但是,您的代码除此之外还有其他问题,即使解决了特定问题也不会编译.首先是一些使其更具可读性的样式问题:
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
具有未使用的参数,该参数应该是State
monad中的一个值,无论如何实际上没有任何意义. - 使用
do
表示符号通常比使用(>>=)
和lambda更清晰,尤其是对于State
之类的东西. - 第二行的缩进令人困惑,因为
in
与位于 lambda内的let
匹配.
getAverage
has an unused parameter, which is supposedly a value in theState
monad, which doesn't really make sense anyway.- Using the
do
notation is usually clearer than using(>>=)
and lambdas, especially for something likeState
. - The indentation of the second line is confusing, since the
in
goes with thelet
that's inside the lambda.
进行这些更改后,我们会得到:
Making those changes we have this:
getAverage s = do
s0 <- get
let (x, s1) = media s s0
put s1
return x
...这使得更容易发现下一个错误:media
的第二个参数是2元组,而s1
只是一个数字,但是您试图将两个都用于状态价值.可能您想要将状态设置为(x, s1)
,但仅返回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. Probably what you wanted was to set the state to (x, s1)
, but return only 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
需要更新整个状态值,因此,只需使用modify
函数,而不是get
ting和put
ting. - 返回值是状态值的第一部分,因此只需
fmap
将fst
替换为get
更为简单.
media
needs to update the entire state value, so rather thanget
ting andput
ting, just use themodify
function.- The return value is the first part of the state value, so just
fmap
ingfst
overget
is more straightforward.
所以现在我们有了这样的东西:
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
我们还可以注意到getAverage
可以做两种不同的事情,并将其拆分为单独的功能:
We can also note that getAverage
is kind of doing two different things, and split it into separate functions:
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
编辑:由于我忘记了将结果实际从单子中取出的小细节,因此在Travis Brown的getAverages
函数中将updateAverage
替换为getAverage
可以使它正常工作在上面的代码中.
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.
这篇关于蒙纳德·哈斯克尔州的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!