Haskellの初期状態を実際に「取得」/取得/する方法は?
-
10-07-2019 - |
質問
機能があります:
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」を取得します。誰かがこれを分解して説明してもらえますか?
ご意見ありがとうございます!
解決
私はもともとこれをコメントとして投稿するつもりでしたが、もう少し詳しく説明することにしました。
厳密に言うと、 get
は"テイク"しません。引数。起こっていることの多くは、見えないもの、つまりStateモナドのインスタンス定義によって隠されていると思います。
get
は、実際にはMonadStateクラスのメソッドです。 StateモナドはMonadStateのインスタンスであり、次の get
の定義を提供します:
get = State $ \s -> (s,s)
つまり、 get
は非常に基本的なStateモナドを返します(モナドは計算の「ラッパー」と考えることができることを思い出してください)、ここで入力 s
を計算に追加すると、結果として s
のペアが返されます。
次に確認する必要があるのは、>> =
で、Stateはこのように定義しています:
m >>= k = State $ \s -> let
(a, s') = runState m s
in runState (k a) s'
そのため、>> =
は新しい計算を生成しますが、初期状態を取得するまで計算されません(これは、すべてのState計算が、 「ラップ」フォーム)。この新しい計算の結果は、左側にあった計算を実行した結果に>> =
の右側にあるものを適用することによって達成されます。 (これは、かなり読みにくい文章で、さらに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
)を必要とする新しい状態計算であることを明らかにする必要があります。 runState
呼び出しで" testtest"
として s
を指定しました。 「testtest」を置き換える場合上記の擬似コードの s
の場合、最初に起こるのは" testtest"で get
を実行することです。 「初期状態」として。これにより、(" testtest"、" testtest")
などが生成されます。
それが get
が初期状態の「testtest」を取得する場所です。これがお役に立てば幸いです!
他のヒント
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
関数は State
タイプの関数の大きな構成であり、それぞれが状態入力を受け取り、(結果、状態)出力を生成し、互いに接続されますプログラムにとって意味のある方法。 runState
は、状態の開始点を提供するだけです。
このコンテキストでは、 get
は単に状態を入力として受け取り、結果が入力状態であり、状態が変更されない(result、state)出力を返す関数です(出力状態は入力状態です)。つまり、 get s =(s、s)
Graham Huttonのの第8章("機能的パーサー")をご覧ください。 Haskellでのプログラミングを何度かプログラミングして、適切に理解した後、チュートリアルモナドのすべて、私のためにこれをクリックしました。
モナドの問題は、通常のプログラミングのバックグラウンドから来た私たちが非常に似ていないものを見つけるいくつかのことに非常に役立つということです。制御フローと処理状態は、同じメカニズムで処理できるほど類似しているだけでなく、十分に後退して同じことを認識していることを理解するには時間がかかります。
Cの制御構造( for
および while
など)を検討していたときに、ひらめきが生じました。最も一般的な制御構造は、単純に1つのステートメントを他のステートメントの前に置きます。 Haskellを研究するのに1年かかりましたが、それが制御構造でさえあることに気付きました。