Skip to content

Latest commit

 

History

History
337 lines (278 loc) · 9.08 KB

Pattern-Matching.md

File metadata and controls

337 lines (278 loc) · 9.08 KB

パターンマッチ

パターンマッチは値を分解して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

(otherwisetrueの別名で、一般的にはガード内で使われます。)

パターンガード

パターンガードは左矢印で表記されるパターンマッチでガードを拡張します。パターンガードは、矢印の右側の計算結果が左側のパターンとマッチする場合にのみ成功します。

例えば、関数fnを引数xに適用することができます。fnJust 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