パターン マッチ (F#)
パターンは、入力データの変換規則です。データを論理構造と比較したり、データを構成要素に分解したり、さまざまな方法でデータから情報を抽出したりするために、F# 言語全体で使用されます。
解説
パターンは、match 式などの多くの言語構成要素で使用されます。let 束縛、ラムダ式、および try...with 式に関連付けられている例外ハンドラーで関数の引数を処理する場合に使用されます。詳細については、「match 式 (F#)」、「let 束縛 (F#)」、「ラムダ式: fun キーワード (F#)」、および「例外: try...with 式 (F#)」を参照してください。
たとえば、match 式では、pattern がパイプ記号の後に続きます。
match expression with
|pattern [ when condition ] -> result-expression
...
各パターンは、なんらかの方法で入力を変換する際の規則として機能します。match 式では、各パターンが順に調べられ、入力データにパターンとの互換性があるかどうかが確認されます。一致が見つかった場合は、結果の式が実行されます。一致が見つからなかった場合は、次のパターン規則がテストされます。オプションの when condition 部分については、「match 式 (F#)」を参照してください。
サポートされているパターンを次の表に示します。実行時に、表に示されている順序で次の各パターンに対して入力がテストされます。パターンは、コードに示されているとおりに先頭から末尾へ、各行のパターンの左から右へ、再帰的に適用されます。
名前 |
Description |
例 |
---|---|---|
定数パターン |
数値、文字、リテラル文字列、列挙定数、または定義済みのリテラル識別子 |
1.0, "test", 30, Color.Red |
識別子パターン |
判別共用体のケース値、例外ラベル、またはアクティブ パターンのケース |
Some(x) Failure(msg) |
変数パターン |
identifier |
a |
as パターン |
pattern as identifier |
(a, b) as tuple1 |
OR パターン |
pattern1 | pattern2 |
([h] | [h; _]) |
AND パターン |
pattern1 & pattern2 |
(a, b) & (_, "test") |
Cons パターン |
identifier :: list-identifier |
h :: t |
リスト パターン |
[ pattern_1; ...; pattern_n ] |
[ a; b; c ] |
配列パターン |
[| pattern_1; ..; pattern_n ] |
[| a; b; c |] |
かっこで囲まれたパターン |
( pattern ) |
( a ) |
タプル パターン |
( pattern_1, ..., pattern_n ) |
( a, b ) |
レコード パターン |
{ identifier1 = pattern_1; ...; identifier_n = pattern_n } |
{ Name = name; } |
ワイルドカード パターン |
_ |
_ |
型の注釈が指定されたパターン |
pattern : type |
a : int |
型テスト パターン |
:?type [ as identifier ] |
:? System.DateTime as dt |
null パターン |
null |
null |
定数パターン
定数パターンは、数値、文字、リテラル文字列、列挙定数 (列挙型名が含まれる) です。定数パターンのみが含まれる match 式は、他の言語の case ステートメントと比較できます。入力がリテラル値と比較され、値が等しい場合にはパターンが一致します。リテラルの型に、入力の型との互換性があることが必要です。
リテラル パターンの使用例を次に示します。変数パターンと OR パターンも使用します。
[<Literal>]
let Three = 3
let filter123 x =
match x with
// The following line contains literal patterns combined with an OR pattern.
| 1 | 2 | Three -> printfn "Found 1, 2, or 3!"
// The following line contains a variable pattern.
| var1 -> printfn "%d" var1
for x in 1..10 do filter123 x
リテラル パターンにはこの他に、列挙定数に基づくパターンもあります。列挙定数を使用するときは、列挙型名を指定する必要があります。
type Color =
| Red = 0
| Green = 1
| Blue = 2
let printColorName (color:Color) =
match color with
| Color.Red -> printfn "Red"
| Color.Green -> printfn "Green"
| Color.Blue -> printfn "Blue"
| _ -> ()
printColorName Color.Red
printColorName Color.Green
printColorName Color.Blue
識別子パターン
パターンが有効な識別子になる文字列の場合は、識別子の形式でパターンの照合方法が決まります。識別子が 2 文字以上で、大文字で始まる場合は、コンパイラが識別子パターンとの照合を試みます。このパターンの識別子は、Literal 属性が指定された値、判別共用体のケース、例外識別子、またはアクティブ パターンのケースです。一致する識別子が見つからない場合は、照合が失敗し、次のパターン規則の変数パターンが入力と比較されます。
判別共用体パターンは、単純な名前付きケースであるか、値またはタプル (複数の値を含む) を含むかのいずれかです。値を含む場合は、値の識別子を指定する必要があり、タプルを含む場合は、タプルの要素ごとに識別子を持つタプル パターンを指定する必要があります。例については、このセクションのコード例を参照してください。
option 型は、Some と None の 2 つのケースを持つ判別共用体です。一方のケース (Some) には値が含まれますが、もう一方のケース (None) は単なる名前付きケースです。したがって、Some には、Some ケースに関連付けられた値の変数が必要ですが、None は単体で使用する必要があります。次のコードでは、var1 変数に、Some ケースとの照合で取得された値が指定されます。
let printOption (data : int option) =
match data with
| Some var1 -> printfn "%d" var1
| None -> ()
次の例では、PersonName 判別共用体に、名前に使用できる形式を表す文字列および文字が混在しています。判別共用体のケースは FirstOnly、LastOnly、および FirstLast です。
type PersonName =
| FirstOnly of string
| LastOnly of string
| FirstLast of string * string
let constructQuery personName =
match personName with
| FirstOnly(firstName) -> printf "May I call you %s?" firstName
| LastOnly(lastName) -> printf "Are you Mr. or Ms. %s?" lastName
| FirstLast(firstName, lastName) -> printf "Are you %s %s?" firstName lastName
アクティブ パターンを使用すると、より複雑なカスタム パターン マッチを定義できます。アクティブ パターンの詳細については、「アクティブ パターン (F#)」を参照してください。
識別子が例外であるケースは、例外ハンドラーのコンテキストのパターン マッチで使用されます。例外処理におけるパターン マッチの詳細については、「例外: try...with 式 (F#)」を参照してください。
変数パターン
変数パターンでは、照合される値が変数名に割り当てられ、その変数名を、-> 記号の右側の実行式で使用できるようになります。変数パターン単体はどの入力にも一致しますが、多くの場合、変数パターンはその他のパターン内で使用されます。このため、タプルや配列などのより複雑な構造体を変数に分解することが可能になります。
タプル パターン内で変数パターンを使用する例を次に示します。
let function1 x =
match x with
| (var1, var2) when var1 > var2 -> printfn "%d is greater than %d" var1 var2
| (var1, var2) when var1 < var2 -> printfn "%d is less than %d" var1 var2
| (var1, var2) -> printfn "%d equals %d" var1 var2
function1 (1,2)
function1 (2, 1)
function1 (0, 0)
as パターン
as パターンは、as 句が追加されたパターンです。as 句は、照合する値を match 式の実行式で使用できる名前に束縛します。または、このパターンが let 束縛で使用される場合は、名前が束縛としてローカル スコープに追加されます。
as パターンの使用例を次に示します。
let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1
OR パターン
OR パターンは、入力データが複数のパターンと一致する場合に、同じコードを結果として実行するときに使用します。OR パターンの両側の型に互換性があることが必要です。
OR パターンの使用例を次に示します。
let detectZeroOR point =
match point with
| (0, 0) | (0, _) | (_, 0) -> printfn "Zero found."
| _ -> printfn "Both nonzero."
detectZeroOR (0, 0)
detectZeroOR (1, 0)
detectZeroOR (0, 10)
detectZeroOR (10, 15)
AND パターン
AND パターンでは、入力が 2 つのパターンと一致する必要があります。AND パターンの両側の型に互換性があることが必要です。
次の例は、このトピック内で後述の「タプル パターン」に示す detectZeroTuple に似ていますが、ここでは、AND パターンを使用して、var1 と var2 の両方を値として取得しています。
let detectZeroAND point =
match point with
| (0, 0) -> printfn "Both values zero."
| (var1, var2) & (0, _) -> printfn "First value is 0 in (%d, %d)" var1 var2
| (var1, var2) & (_, 0) -> printfn "Second value is 0 in (%d, %d)" var1 var2
| _ -> printfn "Both nonzero."
detectZeroAND (0, 0)
detectZeroAND (1, 0)
detectZeroAND (0, 10)
detectZeroAND (10, 15)
Cons パターン
Cons パターンは、リストを、最初の要素である先頭と、残りの要素である末尾を含むリストに分解するために使用されます。
let list1 = [ 1; 2; 3; 4 ]
// This example uses a cons pattern and a list pattern.
let rec printList l =
match l with
| head :: tail -> printf "%d " head; printList tail
| [] -> printfn ""
printList list1
リスト パターン
リスト パターンでは、リストをいくつかの要素に分解できます。リスト パターン自体は、特定の数の要素を含むリストとだけ一致します。
// This example uses a list pattern.
let listLength list =
match list with
| [] -> 0
| [ _ ] -> 1
| [ _; _ ] -> 2
| [ _; _; _ ] -> 3
| _ -> List.length list
printfn "%d" (listLength [ 1 ])
printfn "%d" (listLength [ 1; 1 ])
printfn "%d" (listLength [ 1; 1; 1; ])
printfn "%d" (listLength [ ] )
配列パターン
配列パターンはリスト パターンに似ており、特定の長さの配列を分解するために使用できます。
// This example uses array patterns.
let vectorLength vec =
match vec with
| [| var1 |] -> var1
| [| var1; var2 |] -> sqrt (var1*var1 + var2*var2)
| [| var1; var2; var3 |] -> sqrt (var1*var1 + var2*var2 + var3*var3)
| _ -> failwith "vectorLength called with an unsupported array size of %d." (vec.Length)
printfn "%f" (vectorLength [| 1. |])
printfn "%f" (vectorLength [| 1.; 1. |])
printfn "%f" (vectorLength [| 1.; 1.; 1.; |])
printfn "%f" (vectorLength [| |] )
かっこで囲まれたパターン
かっこでパターンを囲んで、目的の結合規則を得ることができます。次の例では、かっこを使用して、AND パターンと Cons パターンの間の結合規則を制御します。
let countValues list value =
let rec checkList list acc =
match list with
| (elem1 & head) :: tail when elem1 = value -> checkList tail (acc + 1)
| head :: tail -> checkList tail acc
| [] -> acc
checkList list 0
let result = countValues [ for x in -10..10 -> x*x - 4 ] 0
printfn "%d" result
タプル パターン
タプル パターンはタプル形式の入力と一致します。このパターンでは、タプル内の位置ごとにパターン マッチ変数を使用して、タプルを構成要素に分解できます。
タプル パターンの使用例を次に示します。リテラル パターン、変数パターン、およびワイルドカード パターンも使用します。
let detectZeroTuple point =
match point with
| (0, 0) -> printfn "Both values zero."
| (0, var2) -> printfn "First value is 0 in (0, %d)" var2
| (var1, 0) -> printfn "Second value is 0 in (%d, 0)" var1
| _ -> printfn "Both nonzero."
detectZeroTuple (0, 0)
detectZeroTuple (1, 0)
detectZeroTuple (0, 10)
detectZeroTuple (10, 15)
レコード パターン
レコード パターンは、レコードを分解してフィールドの値を抽出するために使用されます。パターンがレコードのすべてのフィールドを参照する必要はありません。省略されたフィールドは照合に使用されないため、抽出されません。
// This example uses a record pattern.
type MyRecord = { Name: string; ID: int }
let IsMatchByName record1 (name: string) =
match record1 with
| { MyRecord.Name = nameFound; MyRecord.ID = _; } when nameFound = name -> true
| _ -> false
let recordX = { Name = "Parker"; ID = 10 }
let isMatched1 = IsMatchByName recordX "Parker"
let isMatched2 = IsMatchByName recordX "Hartono"
ワイルドカード パターン
ワイルドカード パターンは _ アンダースコア () 文字と一致すると変数パターンなどの入力表示入力が変数に割り当てられるの代わりに破棄されます。多くの場合、ワイルドカード パターンは、その他のパターン内で -> 記号の右側の式で不要な値のプレースホルダーとして使用されます。また、一致しなかった入力に対応するために、パターン リストの最後にワイルドカード パターンが使用されることもよくあります。ワイルドカード パターンは、このトピックの多くのコード例で示されています。一例として、上記のコードを参照してください。
型の注釈が付けられたパターン
パターンには型の注釈を付けることができます。このコメントはその他の型の注釈と同様に動作し、その他の型の注釈と同様に推論を導きます。パターンの型の注釈はかっこで囲む必要があります。型の注釈が付けられたパターンを次のコードに示します。
let detect1 x =
match x with
| 1 -> printfn "Found a 1!"
| (var1 : int) -> printfn "%d" var1
detect1 0
detect1 1
型テスト パターン
型テスト パターンは、入力を型と照合するために使用されます。入力の型が一致 (または派生型の) パターンで指定された型と一致と見なされます。
型テスト パターンの使用例を次に示します。
open System.Windows.Forms
let RegisterControl(control:Control) =
match control with
| :? Button as button -> button.Text <- "Registered."
| :? CheckBox as checkbox -> checkbox.Text <- "Registered."
| _ -> ()
null パターン
null パターンは null 値と一致します。null 値は、null 値を許可する型を使用しているときに示される可能性があります。null パターンは、多くの場合、.NET Framework コードとの相互運用時に使用されます。たとえば、.NET API の戻り値が match 式への入力である場合が考えられます。プログラム フローは、戻り値が null であるかどうかと、戻り値のその他の特性に基づいて制御できます。null パターンを使用すると、null 値が残りのプログラムに反映されるのを防ぐことができます。
null パターンと変数パターンの使用例を次に示します。
let ReadFromFile (reader : System.IO.StreamReader) =
match reader.ReadLine() with
| null -> printfn "\n"; false
| line -> printfn "%s" line; true
let fs = System.IO.File.Open("..\..\Program.fs", System.IO.FileMode.Open)
let sr = new System.IO.StreamReader(fs)
while ReadFromFile(sr) = true do ()
sr.Close()