23

私はオンラインブックLearnyoua Haskell forgreatGoodを使用してHaskellを把握しようとしています

私の知る限り、州のモナドを紹介する章にたどり着くまで、これまでモナドを理解することができました。

ただし、提示され、StateタイプのMonad実装であると主張されているコード(Hoogleで見つけることができませんでした)は、処理するには多すぎるようです。

  • そもそも、その背後にある論理、つまり、なぜそれが機能する必要があるのか​​、そして著者がこの手法をどのように検討したのかを理解していません(おそらく関連記事やホワイトペーパーを提案できますか?)

  • 4行目では、関数fが1つのパラメーターを取ることが提案されています。
    ただし、数行下には、パラメーターをとらないpopが表示されます。

  • ポイント1を拡張するために、状態を表す関数を使用して作成者が達成しようとしていることは何ですか。

何が起こっているのかを理解するのに助けていただければ幸いです。

編集

ご担当者様、

以下の答えは私の質問を完全にカバーしています。
ただし、追加したいことが1つあります。

以下に提案する記事を読んだ後、上記の2番目のポイントに対する答えを見つけました:その間ずっと、pop関数次のように使用されると思い
stuff >>= popました:バインドタイプでは2番目のパラメーターは関数ですが、正しい使用法はこれですpop >>= stuff、これをもう一度読んだ後、do-notationがプレーンなbind-lambdasにどのように変換されるかを理解しました。

4

4 に答える 4

20

Stateモナドは、ステートフルな計算、つまり何らかの外部状態からの値を使用し、おそらく変更する計算を表します。ステートフルな計算を一緒に並べると、前の計算で状態がどのように変更されたかによって、後の計算で異なる結果が得られる場合があります。

Haskell の関数は純粋でなければならない (つまり、副作用がない) ため、すべての関数が現在の世界の状態を表す追加のパラメーターを取り、変更された状態を表す追加の値を返すことを要求することで、外部状態の影響をシミュレートします。事実上、MSPaint で描いた図のこの忌まわしき例のように、外部状態は一連の計算を介してスレッド化されます。

ここに画像の説明を入力

各ボックス (計算を表す) に 1 つの入力と 2 つの出力があることに注目してください。

Monadインスタンスを見るStateと、 の定義が(>>=)このスレッド化の方法を示していることがわかります。ステートフルな計算を、ステートフルな計算の結果を受け取り、別のステートフルな計算を返すc0関数にバインドするには、次のようにします。f

  1. c0初期状態を使用して実行しs0、結果と新しい状態を取得します。(val, s1)
  2. val関数にフィードしてf、新しいステートフルな計算を取得します。c1
  3. c1変更された状態で新しい計算を実行しますs1

これは、すでに引数を取る関数でどのように機能しnますか? Haskell のすべての関数はデフォルトでカリー化されているため、追加の引数 (状態用) を最後に追加するだけで、通常の戻り値の代わりに、関数は 2 番目の要素が新しく変更された状態であるペアを返します。だから代わりに

f :: a -> b

私たちは今持っています

f :: a -> s -> (b, s)

あなたは次のように考えることを選択するかもしれません

f :: a -> ( s -> (b, s) )

これはHaskellでも同じことです(関数合成は右結合であるため) 、「f型の引数を取り、aステートフルな計算を返す関数です」と書かれています。Stateモナドについてはこれですべてです。

于 2012-04-19T15:44:58.843 に答える
14

簡潔な答え:

  1. Stateローカル変数で命令型のシステム状態をシミュレートするために、モナドの機能を利用することを意図しています。基本的な考え方は、現在の状態を取得し、各ステップで中間結果とともに新しい状態を返すアクティビティをモナド内に隠すことです (ここではs -> (a,s).
  2. 内にラップされたものと任意の関数を間違えないでくださいState。前者は、あなたが望むどんな型でも構いません (State a状態モナドでそれらを使用したい場合、それらが最終的に何らかの型を生成するという条件で)。後者は type の関数を保持しますs -> (a,s): これはモナドによって管理される状態渡しレイヤーです。
  3. 先ほど述べたように、ラップされた関数は、インスタンスに対して定義されている方法でState実際に生成されます。その役割は、コードの呼び出しを通じて状態を渡すことです。(>>=)returnMonad (State s)

状態モナドで実際に使われている関数から状態パラメータがなくなったのもポイント3です。

長い答え:

State Monad はさまざまな論文で研究されており、Haskell フレームワークにも存在します (現在、適切なリファレンスを覚えていません。できるだけ早く追加します)。

data MyState = ...これは、システムの現在の状態を保持する値を持つ型を考えてください。

一連の関数を介してそれを渡したい場合は、少なくとも現在の状態をパラメーターとして受け取り、その結果とのペアを返すようにすべての関数を記述する必要があります (状態と他の入力に応じて)パラメータ) と新しい (場合によっては変更された) 状態。まあ、これはまさに状態モナドの型が教えてくれることです: s -> (a, s). この例でsMyState、システムの状態を渡すことを意図しています。

でラップされた関数Stateは、結果として新しい状態と中間結果を生成するために必要な現在の状態以外のパラメーターを取りません。例で見たより多くのパラメーターを持つ関数は問題ではありませんdo。モナド内の - 表記でそれらを使用すると、必要なすべての「余分な」パラメーターにそれらを適用するため、それぞれの結果が得られることを意味します。一意の残りのパラメーターが状態である部分的に適用された関数。のモナドインスタンスStateが残りを行います。

モナドで使用される可能性のある関数の型 (実際、モナド内では通常はアクションと呼ばれます) を見ると、結果の型がモナド内でボックス化されていることがわかります。それらにすべてのパラメーターを与えると、実際には結果は返されませんが、(この場合)s -> (a,s)モナドの合成法則に適合する関数が返されます。

計算は、システムの最初の/初期状態をブロック/構成全体に渡すことによって実行されます。

最後に、パラメーターをとらない関数は、戻り値の型がState awhereのような型になりaます: の値コンストラクターをState見ると、これが実際には関数であることが再びわかりますs -> (a,s)

于 2012-04-19T15:05:54.953 に答える
5

Stateモナドは基本的に

type State s a = s -> (a,s)

1 つの状態 ( s) から、目的の結果 ( a) と新しい状態のペアへの関数。実装により、状態のスレッド化が暗黙的に行われ、状態の受け渡しと更新が処理されるため、誤って間違った状態を次の関数に渡すリスクはありません。

したがって、モナド内で引数を取りk > 0、そのうちの 1 つは状態であり、何かと新しい状態のペアを返す関数は、引数をState s取り、モナド アクションを返す関数になりk-1ます (基本的には、1 つの引数、状態、ここ)。

State 以外の設定では、 pop1 つの引数 (状態であるスタック) を取ります。したがって、モナド設定では、明示的な引数を取らないアクションにpopなります。State Stack Int

State明示的な状態の受け渡しの代わりにモナドを使用すると、コードがよりクリーンになり、エラーの可能性が少なくなります。それがStateモナドが達成することです。それがなくてもすべてを実行できますが、面倒でエラーが発生しやすくなります.

于 2012-04-19T15:03:50.637 に答える