パターンマッチは値を分解して0個以上の式をスコープに入れます。パターンマッチはcase
キーワードにより導入されます。
パターンマッチは一般的に次の形式で表されます:
case value of
pattern -> result
...
pattern -> result
既に見たように、パターンマッチは関数の宣言にも用いられます:
fn pattern_1 ... pattern_n = result
パターンは関数を導入する際にも用いられます。例えば:
example x y z = x * y + z
以下のようなパターンが存在します:
- ワイルドカードパターン
- リテラルパターン
- 変数パターン
- 配列パターン
- コンストラクタパターン
- レコードパターン
- 名前付きパターン
ガードとパターンガードもサポートされています。
網羅性検査器は網羅されていないパターンのためにPartial
制約を導入します。
デフォルトでは、Partial
制約が満たされないために、パターンが網羅されていなければなりません。あなたの関数にローカルなPartial
制約を追加することで、エラーを非表示にすることが可能です。
ワイルドカード_
はどんな入力にもマッチし、スコープには何も入れません:
f _ = 0
リテラルパターンは定数とマッチさせるために用いられます:
f true = 0
f false = 1
g "Foo" = 0
g _ = 1
h 0 = 0
h _ = 1
変数パターンはどんな入力にもマッチし、それを変数の名前に束縛します:
double x = x * 2
配列パターンは配列の入力にマッチし、その要素をスコープに入れます。例えば:
f [x] = x
f [x, y] = x * y
f _ = 0
ここで、1つ目のパターンは長さ1の配列にのみマッチし、その最初の要素をスコープに入れます。
2つ目のパターンは2つの要素を持つ配列にマッチし、その1番目と2番めの要素をスコープに入れます。
コンストラクタパターンはデータコンストラクタとその引数にマッチします:
data Foo = Foo String | Bar Number Boolean
foo (Foo s) = true
foo (Bar _ b) = b
レコードパターンはレコードの入力にマッチし、そのプロパティをスコープに入れます:
f { foo: "Foo", bar: n } = n
f _ = 0
このパターンは既に紹介したパターンを組み合わせて表現されます。例えば:
f { arr: [x, _], take: "firstOfTwo" } = x
f { arr: [_, x, _], take: "secondOfThree" } = x
f _ = 0
名前付きパターンはネストしたパターンを使っているときに、新たな名前をスコープに入れます。@
記号を用いてどんなパターンも名付けられます:
f a@[_, _] = a
f _ = []
ここで、1つ目のパターンでは、正確に2つの要素を持つ配列にマッチし、配列は変数a
に束縛されます。
ガードは条件式を用いて追加の制約を課すために用いられ、パターンの後にパイプを書くことで導入されます:
evens :: List Int -> Int
evens Nil = 0
evens (Cons x xs) | x `mod` 2 == 0 = 1 + evens xs
evens (Cons _ xs) = evens xs
トップレベルで関数を定義するためにパターンを用いるとき、ガードは全パターンの後に現れます:
greater x y | x > y = true
greater _ _ = false
網羅的に考えるために、ガードは常に明らかに真になる条件を含まなければなりません。以下を完璧に作っても、コンパイラはそれが網羅的であることを検出できません:
compare :: Int -> Int -> Ordering
compare x y
| x > y = GT
| x == y = EQ
| x < y = LT
以下はどちらも動き、最後のケースを含むことで網羅性が明確になっています:
compare x y
| x > y = GT
| x < y = LT
| otherwise = EQ
compare x y | x > y = GT
compare x y | x < y = LT
compare _ _ = EQ
(otherwise
はtrue
の別名で、一般的にはガード内で使われます。)
パターンガードは左矢印で表記されるパターンマッチでガードを拡張します。パターンガードは、矢印の右側の計算結果が左側のパターンとマッチする場合にのみ成功します。
例えば、関数fn
を引数x
に適用することができます。fn
がJust y
を返すときにのみ成功します。y
は同時に束縛されます:
bar x | Just y <- fn x = ... -- x and y are both in scope here
パターンガードは、代数的データ型を使用する時、制御フローの特定の型を表現するために特に有用です。
ガードに複数の式を追加するためにカンマを使うこともできます:
positiveLessThanFive :: Maybe Int -> Boolean
positiveLessThanFive mInt
| Just x <- mInt
, x > 0
, x < 5 = true
| otherwise = false