状态单子哈斯克尔

发布于 2024-09-12 04:29:44 字数 384 浏览 3 评论 0原文

我想编写一个函数来使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

天邊彩虹 2024-09-19 04:29:44

您提供的代码给出了此错误:

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 表示法通常比使用 (>>=) 和 lambda 更清晰,尤其是对于 State 之类的东西。
  • 第二行的缩进令人困惑,因为 in 与 lambda 内部let 相匹配。

进行这些更改后,我们得到了:

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 需要更新整个状态值,所以不是 getting 和 putting,只需使用修改功能即可。
  • 返回值是状态值的第一部分,因此只需通过 get 进行 fmap ing fst 就更简单了。

所以现在我们有了这样的东西:

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

编辑:因为我忘记了实际上从 monad 中获取结果的小细节,在 Travis Brown 的 getAverages 函数中用 getAverage 替换 updateAverage 将让它在我的上面的代码。

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.

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.
  • Using the do notation is usually clearer than using (>>=) and lambdas, especially for something like State.
  • The indentation of the second line is confusing, since the in goes with the let 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

...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 needs to update the entire state value, so rather than getting and putting, just use the modify function.
  • The return value is the first part of the state value, so just fmaping fst over get 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

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

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.

醉生梦死 2024-09-19 04:29:44

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


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

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

这仍然没有编译,因为我们试图放入一些没有正确类型的东西:s1是一个Double,而不是一个我的状态。这很容易解决:

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 MyStateState MyState 的实例>MonadState (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< /code> (或密切相关的 runStateexecState)。

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:

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. 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.

This compiles, so now we can fix the type signature. If we ask GHCi for the type, it returns the following:

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:

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 Doubles, runs them through updateAverage, and returns a list of the intermediate averages at each step:

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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文