4

私はリストとモナドと完全に混同しているので、おそらく私の質問は正しくないか、非常に素朴です. ここでmapM_ func を使用してそれを行う方法を見てきました:

mapM_ print [1, 2, 3, 4]

しかし、それがどのように機能するのか正確にはわかりません。次のような方法でこれを行う方法を知りたいです:

x <- [1, 2, 3]
print x

または、私がそれを正しく理解していれば:

[1, 2, 3] >>= print

[1, 2, 3] には type が[a]あり、 print には typeがあることを理解していShow a => a -> IO ()ます。また、モナド List を使用するList aには、左側に type が必要であり、右側に func with type が必要であることも理解しa -> List bています。私は正しいですか?これで私を助けてもらえますか?

UPD。mapM_ の仕組みを説明してくれた @MathematicalOrchid に感謝します。私の側から説明したいのですが、実際の問題は結果を別の行に出力することではなく、モナド List が提供する方法でいくつかのモナド アクションを実行することです (今は OpenGL のことをぶらぶらしているため)。しかし、誤解の根源はモナドの混合にあることがわかりました。

UPD2。回答ありがとうございます。このようなあいまいな質問で申し訳ありません。必要な答えと質問が何であるかが正確にはわかりません。それは、私がいくつかの基本を理解していなかったからです。そのため、すべての答えには、私が探していたものの小さな平和があるため、「正しい答え」を選択するのは難しいです。私は、私が望んでいたものに最も近いものを選択することにしました (現在は最も有用ではありませんが)。

4

5 に答える 5

13

ここでいくつかのことが混乱しているようです。(特に、リストはモナドを形成し、I/O は別のモナドを形成します。) これを解決しようとします...

まず第一に、print関数は表示可能なものをすべて取り、それを標準出力に書き込み、その後に改行を続けます。それでprint [1, 2, 3]問題なく動作しますが、明らかにすべてを同じ行に書き込みます。別々の行に何かを書くには、printアイテムごとに別々の呼び出しが必要です。ここまでは順調ですね。

このmap関数は、リストのすべての要素に関数を適用します。したがって、リスト内の各項目にmap print [1, 2, 3]適用されます。ただし、結果は I/O アクションのリストです。そして、それは私たちが求めているものではありません。これらのアクションをリストするのではなく、実行したいのです。print

これを行う方法は、2 つの I/O アクションを連鎖させる演算子を使用する>>ことです (結果に関心がなく、何かを出力しても何も返されない場合)。したがってfoldr (>>) (return ())、I/O アクションのリストを取得して、それを 1 つの I/O アクションに変換します。この関数は実際にはすでに定義されています。と呼ばれsequenceます。

ただし、map+sequenceは非常に一般的な組み合わせであるため、これも既に定義されています。と呼ばれmapM_ます。(mapM結果を保持したい場合は、アンダースコアなしの もあります。ただし、印刷では何も返されないため、必要ありません。)


さて、それが機能する理由mapM_です。ここで、他のいくつかの方法がうまくいかない理由を尋ねます...

x <- [1, 2, 3]
print x

これはまったく機能しません。最初の行はリストモナドです。しかし、2 行目は I/O モナドにあります。そんなことはできません。(やや不可解な型チェッカー エラーが表示されます。) これは Haskell のいわゆる「do 記法」であり、上記のフラグメントがdo実際に有効な構文であるためには、先頭にキーワードが必要であることを指摘しておく必要があります。

do
  x <- [1, 2, 3]
  print x

いずれにせよ、まだうまくいきません。それはほとんどのことをmap print [1, 2, 3]行いますが、完全ではありません。(私が言ったように、型チェックはしません。)

[1, 2, 3] >>= print前のスニペットと同じも提案しました。(実際、コンパイラは前者を後者に変換します。) 同じ理由で、オリジナルは型チェックを行わず、これも型チェックを行いません。

これは、行列に数値を追加しようとするのと少し似ています。数字は加算可能なものです。行列は追加可能なものです。ただし、それらは同じではないため、一方を他方に追加することはできません。それが理にかなっていれば。

于 2012-09-17T15:15:27.680 に答える
10

2つのモナドを混ぜ合わせようとしているため、このように機能させることはできません。

do x <- [1,2,3]
   print x

具体的には、IO[]モナドを混合しています。do-notationでは、すべてのステートメントがm a一部のモナドのタイプである必要がありmます。ただし、上記のコードでは、最初のステートメントのタイプはタイプ[Integer]で、2番目のステートメントのタイプはIO ()です。

ListT必要な効果を得るには、モナド変換子を使用する必要があります。モナド変換子を使用すると、モナドを特定の順序でスタックに混合し、必要に応じてそれらの効果を組み合わせることができます。

import Control.Monad.Trans
import Control.Monad.Trans.List

value = do x <- ListT (return [1,2,3])
           lift (print x)

これにより、タイプの値が返されますListT IO IntegerIOこのトランスフォーマーから計算を取得するには、を使用しますrunListT。タイプの値を返しますIO [Integer]。これは出力します:

GHCI> runListT value
1
2
3
[(),(),()]

これはに相当しmapM print [1,2,3]ます。リストを破棄して効果を得るには、からmapM_ print [1,2,3]使用できます。voidControl.Monad

GHCI> void . runListT $ value
1
2
3
于 2012-09-17T15:34:39.403 に答える
5

アクションを順番sequence_に実行するために使用できます。IO

sequence_ $ [1, 2, 3] >>= (\x -> [print x])

しかし、私mapM_はかなり明確だと思います。

于 2012-09-17T15:16:30.503 に答える
4

質問自体が少し間違っていると思うので、あなたの質問に正確に答えるつもりはありません。特に、mapMまたは類似のものを使用することは、ここで行うべき正しいことです。この作業に do 記法を使用すると、作業が複雑になるだけです。しかし、消化しやすい代替案を提供します。

必須のバックグラウンドを持っている場合 (つまり、C、Java、Python などに精通している場合)forMは、mapM. 構文は次のとおりです。

forM <list of things> <action to perform for each thing>

つまり、for-each ループのようなものです。例えば:

ghci> import Control.Monad
ghci> forM [1,2,3] print
1
2
3
[(),(),()]

物のリストは で[1,2,3]、それぞれに対して実行するアクションは ですprint。最後の戻り値に注目してください。これは、各呼び出しがprintreturn()であり、それらが最後にまとめられるためです。戻り値が必要ない場合は、次のようforM_に の代わりに使用します。forM

ghci> forM_ [1,2,3] print
1
2
3

秘密の準備はできていますか?関数forMforM_mapMmapM_引数が逆になっています。つまり、次のとおりです。

forM list action = mapM action list

多くの場合、必要なリストではなく関数forMに注意を引くため、コードでよく使用します。関数が複数の行にまたがると、見た目もすっきりします。

于 2012-09-17T16:03:25.900 に答える
1

これがおそらくどのように機能するかについての最も簡単な説明ですmapM_

main = foldr1 (>>) (map print [1, 2, 3])

つまり、print各リストメンバーに適用され、結果はを使用して結合される>>ため、最初に次のようになります。

main = foldr1 (>>) [print 1, print 2, print 3]

そして最後にあなたは得る

main = print 1 >> print 2 >> print 3 

もう少し正確な説明はこれです:

main = foldr (>>) (return ()) (map print [1, 2, 3])

だから最終的にあなたは得る

main = print 1 >> print 2 >> print 3 >> return ()

このreturn ()部分により、関数は空のリストで機能します。foldr1同じように空のリストでクラッシュするだけheadですtail

于 2012-09-18T11:49:49.877 に答える