2

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 でコンパイルしたときにこのエラーが発生しました。何が問題なのかを理解するのを手伝ってくれませんか。よろしくお願いします

4

2 に答える 2

6

あなたが提供したコードは、このエラーを与えます:

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ため、非モナド型に評価できないため、型シグネチャが正しくありません。

ただし、コードにはそれ以外の問題があり、その特定の問題を修正した後でもコンパイルされません。最初に、読みやすくするためのいくつかの文体上の問題があります。

  • getAverageモナドの値であると思われる未使用のパラメーターがありますが、Stateとにかく意味がありません。
  • do通常、表記法を使用することは(>>=)、特に のようなものでは、 と ラムダを使用するよりも明確ですState
  • 2 行目のインデントは紛らわしいです。これは、inがラムダletにあると一致するためです。

これらの変更を行うと、次のようになります。

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

...これにより、次のエラーを簡単に見つけることmedias1できます。おそらくあなたが望んでいたのは、状態を に設定することでしたが(x, s1)、 のみを返しxます。

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

これは問題なくコンパイルされますが、まだ整理が必要です。

  • media状態値全体を更新する必要があるため、getting とputting ではなく、単に関数を使用しmodifyます。
  • 戻り値は状態値の最初の部分なので、fmaping fstovergetの方が簡単です。

したがって、次のようなものがあります。

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は 2 つの異なることを行うようなものであり、それを別々の機能に分割することもできます。

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 の関数をに置き換えるupdateAverageと、上記のコードで動作するようになります。getAveragegetAverages

于 2010-07-30T17:46:45.933 に答える
3

注: camccann の回答は私の回答よりも優れていますが、私の回答は少し異なるアプローチを取り、状態モナドを評価する方法の例を示しているため、参考のためにここに残します。


関数に表示されないの型シグネチャgetAverageと引数 ( ) を削除することで、問題の解明を開始できます。c

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

put正しい型を持たない何かをしようとしてs1いるため、これはまだコンパイルされませDoubleMyState。これは簡単に修正できます。

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

letまた、パターンを変更せずに、次のように言うput (x,s1)こともできs1ますs0

これはコンパイルされるので、型シグネチャを修正できます。タイプを GHCi に尋ねると、次のように返されます。

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

Doubleは のインスタンスでありFractionalState 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関数を定義できます。DoubleupdateAverage

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モナドevalState(または密接に関連するrunStateand ) を常に使用する必要があることに注意してくださいexecState

于 2010-07-30T18:01:57.073 に答える