12

私は機能を持っています:

test :: String -> State String String
test x = 
    get >>= \test ->
    let test' = x ++ test in
    put test' >>
    get >>= \test2 -> put (test2 ++ x) >>
    return "test"

私はこの関数全体で何が起こっているのかをほとんど理解でき、モナドのコツをつかみ始めています。私が理解していないのは、これを実行するときの方法です:

runState (test "testy") "testtest"

「test」の「get」関数は、どういうわけか初期状態「testtest」を取得します。誰かがこれを分解して説明してもらえますか?

返信ありがとうございます!

4

3 に答える 3

18

もともとコメントとして投稿するつもりでしたが、もう少し詳しく説明することにしました。

厳密に言えば、get議論を「とる」ことはありません。起こっていることの多くは、あなたが見ていないもの、つまり州のモナドのインスタンス定義によって隠されていると思います。

get実際にはMonadStateクラスのメソッドです。StateモナドはMonadStateのインスタンスであり、次の定義を提供しますget

get = State $ \s -> (s,s)

言い換えるとget、非常に基本的な状態モナドを返すだけです(モナドは計算の「ラッパー」と見なすことができることを思い出してください)。ここで、計算への入力は結果としてsペアを返します。s

次に確認する必要があるのは>>=、Stateがこのように定義していることです。

m >>= k  = State $ \s -> let
    (a, s') = runState m s
    in runState (k a) s'

したがって、>>=新しい計算が生成されます。これは、初期状態になるまで計算されません(これは、「ラップされた」形式のすべての状態計算に当てはまります)。>>=この新しい計算の結果は、左側にあった計算を実行した結果に、の右側にあるものをすべて適用することによって実現されます。(これはかなり紛らわしい文章であり、さらに1、2回読む必要があるかもしれません。)

起こっていることすべてを「脱糖」することは非常に有用であることがわかりました。そうすることで、より多くの入力が必要になりますが、あなたの質問への答え(どこgetから来ているのか)が非常に明確になるはずです。以下は擬似コードと見なされるべきであることに注意してください...

test x =
    State $ \s -> let
        (a,s') = runState (State (\s -> (s,s))) s  --substituting above defn. of 'get'
        in runState (rightSide a) s'
        where 
          rightSide test = 
            let test' = x ++ test in
            State $ \s2 -> let
            (a2, s2') = runState (State $ \_ -> ((), test')) s2  -- defn. of 'put'
            in runState (rightSide2 a2) s2'
          rightSide2 _ =
            -- etc...

sこれにより、関数の最終結果が新しいState計算であり、残りの処理を実行するために初期値()が必要になることが明らかになります。あなたはあなたの呼び出しとs同じように供給しました。上記の擬似コードで「testtest」を置き換えると、最初に発生するのは「初期状態」として「testtest」を使用して実行することです。これにより、などが得られます。"testtest"runStatesget("testtest", "testtest")

ここでget、初期状態の「テストテスト」を取得します。お役に立てれば!

于 2009-06-24T10:40:33.910 に答える
5

型コンストラクターが実際に何であるかState、および runState がそれをどのように使用するかを詳しく調べると役立つ場合があります。GHCiでは:

Prelude Control.Monad.State> :i State
newtype State s a = State {runState :: s -> (a, s)}
Prelude Control.Monad.State> :t runState
runState :: State s a -> s -> (a, s)

State状態の型と返される型の 2 つの引数を取ります。これは、初期状態を受け取り、戻り値と新しい状態を生成する関数として実装されます。

runStateそのような関数、初期入力を取り、(おそらく) 一方を他方に適用して (結果、状態) ペアを取得します。

あなたの関数は、それぞれが状態入力を取り、(結果、状態) 出力を生成し、プログラムにとって意味のある方法で互いにプラグインされたタイプの関数testの大きな構成です。状態の開始点を提供するだけですStaterunState

このコンテキストでgetは、 は入力として状態を取り、結果が入力状態であり、状態が変更されない (出力状態が入力状態である) ような (result,state) 出力を返す単純な関数です。言い換えると、get s = (s, s)

于 2009-06-24T08:05:34.550 に答える
1

Graham Hutton's Programming in Haskellの第 8 章 (「Functional Parsers」) を適切に理解するまで何度か読み、続いてチュートリアルの All About Monadsを読んで、これをクリックしました。

モナドの問題は、通常のプログラミングのバックグラウンドを持つ私たちがまったく似ていないと感じるいくつかのことに対してモナドが非常に役立つことです。制御フローと処理状態は、同じメカニズムで処理できるほど似ているだけでなく、一歩下がって同じことを理解するのに時間がかかります。

forC の制御構造 ( andなど)を考えていたときにひらめきましたwhile。最も一般的な制御構造は、単に 1 つのステートメントを別のステートメントの前に置くだけであることに気付きました。Haskell を勉強して、それが制御構造であることに気付くまでに 1 年かかりました。

于 2009-06-24T18:52:03.603 に答える