2009-03-16 7 views
20

私は、パターンマッチングに関して、Haskellのリストの理解がどのように「フードの下で」機能しているかを理解しようとしています。次GHCiの出力は私のポイントを示していますパターンマッチが失敗した場合、Haskellのリスト内包表記でエラーが発生するのはなぜですか?

Prelude> let myList = [Just 1, Just 2, Nothing, Just 3] 
Prelude> let xs = [x | Just x <- myList] 
Prelude> xs 
[1,2,3] 
Prelude> 

あなたが見ることができるように、「何を」スキップないとだけ「ジャスト」の値を選択することができます。私はそのリストのように定義されたモナド、であることを理解(実世界Haskellの、CHからソース14。):

instance Monad [] where 
    return x = [x] 
    xs >>= f = concat (map f xs) 
    xs >> f = concat (map (\_ -> f) xs) 
    fail _ = [] 

そのため、リスト内包表記は、基本的にはリスト内包で選択したすべての要素のシングルトンリストを構築し、それらを連結します。あるステップでパターンマッチが失敗した場合は、代わりに「失敗」関数の結果が使用されます。言い換えると、 "Just x"パターンが一致しないので、[concat]が呼び出されるまで[]がプレースホルダとして使用されます。これは、「何もない」がスキップされたように見える理由を説明します。

私が理解できないことは、ハスケルが「失敗」機能を呼び出すことをどのように知っているかということです。それは "コンパイラの魔法"ですか、それともHaskellで自分自身で書くことができる機能ですか?リストの理解と同じように機能するために、次の「選択」機能を書くことは可能ですか?

select :: (a -> b) -> [a] -> [b] 
select (Just x -> x) myList  -- how to prevent the lambda from raising an error? 
[1,2,3] 

答えて

29

ハスケルのimplemenatationsが内部で直接このようにそれをしないかもしれませんが、それはそれについてこのように考えるのが便利です:)

[x | Just x <- myList] 

...次のようになります。

do 
    Just x <- myList 
    return x 

...これは次のとおりです。あなたの質問に関しては

myList >>= \(Just x) -> return x 

私が理解できないことは、ハスケルが "失敗"関数を呼び出すことをどのように知っているかです。

do表記では、パターンバインディングが失敗すると(つまり、Just x)、failメソッドが呼び出されます。

myList >>= \temp -> case temp of 
    (Just x) -> return x 
    _  -> fail "..." 

だから、あなたが失敗することがありモナド文脈におけるパターンマッチを持っているたびに、Haskellはfailへの呼び出しを挿入します。上記の例の場合、それは次のようになります。

main = do 
    (1,x) <- return (0,2) 
    print x -- x would be 2, but the pattern match fails 
+0

ありがとうございます!このレスポンスは、私が読んできたものと一致し、list以外のタイプのパターンマッチの動作を説明します。コンパイラがこのようなcase文を使って、いつ呼び出すべきかを判断するのは私には起こりませんでした。これは完全に理にかなっています。 – Cybis

6

私はリスト内包構文はリスト([])、またはそのことについてMaybeは、Monad型クラスのインスタンスであることを起こるということと大いに関係しているとは思いません。

リストの内包表記は確かにコンパイラ魔法またはシンタックスシュガーですが、コンパイラは[]データ型の構造を知っているので、それが可能です。ここで

はリスト内包がにコンパイルされたものです。(まあ、私が思うに、私は実際にGHCに対してそれをチェックしませんでした)

xs = let f = \xs -> case xs of 
         Just x -> [x] 
         _  -> [] 
    in concatMap f myList 

あなたが見ることができるように、コンパイラが呼び出す必要はありません。 fail関数では、リストであることが分かっているため、空のリストをインライン化できます。興味深いことに、リスト内包構文の「スキップ」パターン一致失敗が、いくつかのライブラリでジェネリックプログラミングを行うために使用されているという興味深い事実です。 Uniplate libraryの例を参照してください。


編集:ああ、あなたの質問に答えるために、あなたはそれを与えたラムダであなたのselect関数を呼び出すことはできません。 Nothingの値で呼び出すと、パターンマッチの失敗で失敗します。

あなたはそれを上記のコードからf関数を渡すことができますが、selectよりもタイプだろう:

まったく問題あり
select :: (a -> [b]) -> [a] -> [b] 

は、あなたが内部

:-) concatMap機能を使用することができますがまた、新しいselectは今リストのモナドバインド演算子の種類を持っていること(その引数で裏返し):

(>>=) :: [a] -> (a -> [b]) -> [b] 
xs >>= f = concatMap f xs -- 'or as you said: concat (map f xs) 
+1

良い説明、ありがとう:IOでそれを試してみてください。リストの解説についてhttp://en.wikibooks.org/wiki/Haskell/Pattern_matchingで読んだことは間違っているはずです(wikiなので、私は驚いていません)。 – Cybis

+0

申し訳ありませんがあなたの答えは受け入れられませんでしたが、なぜリストモントがリスト内包物に似ているのかを説明するために、ポルジスはより正確です。 – Cybis

+0

リスト内包表記がパターンマッチの失敗をスキップする方法について特別なことは何もないと言いたいだけです。これは、さまざまな方法で行うことができます。そのuniplateの例は、 'variables x = universe x >> = \ case Var x - > [x];というように簡単に書くことができます。 _ - > [] 'またはパターンマッチングの他の形式で指定します。 – semicolon

10

rule for desugaring a list comprehension言語理由から取り出しmonad comprehensionsを持っていたハスケルの

let ok p = [e] 
    ok _ = [] 
in concatMap ok l 

以前のバージョンのように振る舞う(リスト発現をe式、pパターンであり、l)形態[ e | p <- l ]の発現を必要とdo -notationで読みにくく重複していました。 (リストの内包表記は、あまりにも、冗長ですが、彼らが読みそれほど困難ではありません。)私は(ゼロとモナドとして、または、正確には)モナドとして脱糖[ e | p <- l ]思い

のようなものをもたらすであろう
let ok p = return e 
    ok _ = mzero 
in l >>= ok 

mzeroMonadPlusクラスからです。これは

我々はリストモナドを取るとき

let ok p = return e 
    ok _ = fail "..." 
in l >>= ok 

からdesugarsは、我々は

return e = [e] 
mzero = fail _ = [] 
(>>=) = flip concatMap 

すなわち、3つのアプローチ(リスト内包、モナド内包表記を、持っている

do { p <- l; return e } 

に非常に近いですdo式)はリストと同等です。

+0

'e'は必ずしもList型ではありません。それは "ok ok p = [e]"ではないでしょうか?それはうまくいくでしょう。それにかかわらず、あなたの言うことが正しいならば、http://en.wikibooks.org/wiki/Haskell/Pattern_matchingは間違っていなければなりません。 – Cybis

+0

あなたが正しいです、私は翻訳を間違えました。 –

+0

この報告書は、「リスト内包はこれらのアイデンティティーを満たす」と述べています。彼らはちょうどあなたが持っているかのように働く必要があります:) – porges