状态单子哈斯克尔
我想编写一个函数来使用 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 中编译时出现此错误,我卡在那里 你能帮助我了解问题所在吗,提前谢谢你
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
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您提供的代码给出了此错误:
所有这意味着表达式(“推断”)产生的类型与类型签名(“预期”)不一致。在本例中,
getAverage
在State
monad 中运行,因此类型签名不正确,因为它无法计算为非 Monadic 类型。然而,除此之外,您的代码还有其他问题,即使在修复该特定问题后也无法编译。首先是一些风格问题,以使其更具可读性:
getAverage
有一个未使用的参数,据说它是State
monad 中的一个值,无论如何它实际上没有意义。do
表示法通常比使用(>>=)
和 lambda 更清晰,尤其是对于State
之类的东西。in
与 lambda 内部 的let
相匹配。进行这些更改后,我们得到了:
...这使得更容易发现下一个错误:
media
的第二个参数是一个 2 元组,而s1
只是一个单个数字,但您尝试将两者用作状态值。也许您想要的是将状态设置为(x, s1)
,但仅返回x
。编译得很好,但仍然需要一些整理:
media
需要更新整个状态值,所以不是get
ting 和put
ting,只需使用修改
功能即可。get
进行fmap
ingfst
就更简单了。所以现在我们有了这样的东西:
我们还可以注意到
getAverage
是在做两件不同的事情,并将其分成单独的函数:编辑:因为我忘记了实际上从 monad 中获取结果的小细节,在 Travis Brown 的
getAverages
函数中用getAverage
替换updateAverage
将让它在我的上面的代码。The code you provided gives this error:
All this means is that the type resulting from the expression ("inferred") disagrees with the type signature ("expected"). In this case,
getAverage
operates in theState
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
has an unused parameter, which is supposedly a value in theState
monad, which doesn't really make sense anyway.do
notation is usually clearer than using(>>=)
and lambdas, especially for something likeState
.in
goes with thelet
that's inside the lambda.Making those changes we have this:
...which makes it easier to spot the next error: The second argument of
media
is a 2-tuple, ands1
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 onlyx
.This compiles just fine, but still needs some tidying:
media
needs to update the entire state value, so rather thanget
ting andput
ting, just use themodify
function.fmap
ingfst
overget
is more straightforward.So now we have something like this:
We can also note that
getAverage
is kind of doing two different things, and split it into separate functions:Edit: And since I forgot about the minor detail of actually getting the results back out of the monad, replacing
updateAverage
withgetAverage
in Travis Brown'sgetAverages
function will let it work on my code above.注意:camccann 的答案比我的好,但我的方法略有不同,并给出了如何评估状态单子的示例,所以我将其留在这里供参考。
我们可以通过删除
getAverage
的类型签名和函数中未出现的参数 (c
) 来开始尝试找出问题:这仍然没有编译,因为我们试图
放入
一些没有正确类型的东西:s1
是一个Double
,而不是一个我的状态
。这很容易解决:我们也可以保持
let
模式不变,只说put (x,s1)
:我这样做是为了让我们的>s1
与s0
具有相同的类型。这可以编译,所以现在我们可以修复类型签名。如果我们向 GHCi 询问类型,它会返回以下内容:
Double
是Fractional
的实例,State MyState
是State MyState 的实例>MonadState (Double, Double)
,因此我们可以使用与您的原始类型非常相似的东西来进行getAverage
:此函数并不真正“获取”平均值:它会在添加后更新平均值一个新值,所以让我们适当地重命名它:
现在我们可以定义一个
getAverages
函数,它接受一个Double
列表,通过updateAverage
运行它们,并返回每一步的中间平均值列表:这符合我们的预期:
请注意,要使用
State
monad 执行任何有用的操作,您始终必须使用evalState< /code> (或密切相关的
runState
和execState
)。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.
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:This still doesn't compile, because we're trying to
put
something that doesn't have the right type:s1
is aDouble
, not aMyState
. This is easily fixable:We could also leave the
let
pattern unchanged and just sayput (x,s1)
: I'm doing it this way instead so that ours1
has the same type ass0
.This compiles, so now we can fix the type signature. If we ask GHCi for the type, it returns the following:
Double
is an instance ofFractional
, andState MyState
is an instance ofMonadState (Double, Double)
, so we can use something very similar to your original type forgetAverage
:This function doesn't really "get" the average: it updates it after adding a new value, so let's rename it appropriately:
Now we can define a
getAverages
function that takes a list ofDouble
s, runs them throughupdateAverage
, and returns a list of the intermediate averages at each step:This does what we'd expect:
Note that to do anything useful with the
State
monad you'll always have to useevalState
(or the closely relatedrunState
andexecState
).