パターン マッチング
パターンは、入力データを変換するためのルールです。 F# 全体で、データを論理構造または構造と比較したり、データを構成要素に分解したり、さまざまな方法でデータから情報を抽出したりするために使用されます。
解説
パターンは、match
式など、多くの言語コンストラクトで使用されます。 これらは、let
バインド、ラムダ式、および try...with
式に関連付けられている例外ハンドラーで関数の引数を処理するときに使用されます。 詳細については、「match 式」、「let 束縛」、「ラムダ式: fun
キーワード」、および「例外: try...with
式」を参照してください。
たとえば、match
式では、パターン はパイプ記号の後に続きます。
match expression with
| pattern [ when condition ] -> result-expression
...
各パターンは、何らかの方法で入力を変換するためのルールとして機能します。 match
式では、各パターンが順番に調べ、入力データがパターンと互換性があるかどうかを確認します。 一致が見つかった場合は、結果式が実行されます。 一致が見つからない場合は、次のパターン ルールがテストされます。 condition 部分が match 式で説明されている場合は、省略可能です。
サポートされているパターンを次の表に示します。 実行時に、入力は次の各パターンに対して表に示されている順序でテストされ、パターンはコードに表示されるときに最初から最後まで再帰的に適用され、各行のパターンは左から右に適用されます。
名前 | 説明 | 例 |
---|---|---|
定数パターン | 任意の数値、文字、または文字列リテラル、列挙定数、または定義済みのリテラル識別子 | 1.0 、 "test" 、 30 、 Color.Red |
識別子パターン | 判別共用体のケース値、例外ラベル、またはアクティブ パターンのケース | Some(x) Failure(msg) |
変数パターン | 識別子 | a |
as パターン |
pattern as identifier | (a, b) as tuple1 |
ORのパターン | pattern1 | pattern2 | ([h] | [h; _]) |
AND パターン | pattern1 & pattern2 | (a, b) & (_, "test") |
Cons パターン | 識別子 :: リスト識別子 | 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 |
Nameof パターン | nameof expr | nameof str |
定数パターン
定数パターンは、数値、文字、および文字列リテラル、列挙定数 (列挙型名を含む) です。 定数パターンのみを持つ 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
リテラル パターンのもう 1 つの例は、列挙定数に基づくパターンです。 列挙型定数を使用する場合は、列挙型名を指定する必要があります。
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
識別子パターン
パターンが有効な識別子を形成する文字の文字列である場合、識別子の形式によってパターンの照合方法が決まります。 識別子が 1 文字より長く、大文字で始まる場合、コンパイラは識別子パターンに一致しようとします。 このパターンの識別子は、Literal 属性が指定された値、判別共用体のケース、例外識別子、またはアクティブ パターンのケースです。 一致する識別子が見つからない場合、一致は失敗し、次のパターン ルールである変数パターンが入力と比較されます。
判別共用体パターンは、単純な名前付きケースであるか、値またはタプル (複数の値を含む) を含むかのいずれかです。 値がある場合は、値の識別子を指定する必要があります。 タプルの場合は、タプルの各要素の識別子を持つタプル パターン、または 1 つ以上の名前付き共用体フィールドのフィールド名を持つ識別子を指定する必要があります。 例については、このセクションのコード例を参照してください。
option
型は、Some
と None
の 2 つのケースを持つ判別共用体です。 1 つのケース (Some
) には値がありますが、もう 1 つのケース (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
名前付きフィールドを持つ判別共用体の場合は、等号 (=) を使用して名前付きフィールドの値を抽出します。 たとえば、次のような宣言を持つ判別共用体について検討します。
type Shape =
| Rectangle of height : float * width : float
| Circle of radius : float
パターン マッチング式では、次のように名前付きフィールドを使用できます。
let matchShape shape =
match shape with
| Rectangle(height = h) -> printfn $"Rectangle with length %f{h}"
| Circle(r) -> printfn $"Circle with radius %f{r}"
名前付きフィールドの使用は省略可能であるため、前の例では、Circle(r)
と Circle(radius = r)
の両方が同じ効果を持ちます。
複数のフィールドを指定する場合は、セミコロン (;)を区切り記号として使用します。
match shape with
| Rectangle(height = h; width = w) -> printfn $"Rectangle with height %f{h} and width %f{w}"
| _ -> ()
アクティブ パターンを使用すると、より複雑なカスタム パターン マッチングを定義できます。 アクティブ パターンの詳細については、「アクティブ パターン 」を参照してください。
識別子が例外であるケースは、例外ハンドラーのコンテキストでのパターン マッチングで使用されます。 例外処理でのパターン マッチングの詳細については、「例外: try...with
式」を参照してください。
変数パターン
変数パターンは、一致する値を変数名に割り当てます。この値は、->
シンボルの右側にある実行式で使用できます。 変数パターンだけがすべての入力に一致しますが、変数パターンは他のパターン内に出現することが多いため、タプルや配列などのより複雑な構造を変数に分解できます。
次の例は、タプル パターン内の変数パターンを示しています。
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
に似ていますが、ここでは、var1
と var2
の両方を AND パターンを使用して値として取得します。
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 (sprintf "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"
ワイルドカード パターン
ワイルドカード パターンはアンダースコア (_
) 文字で表され、変数パターンと同様に任意の入力と一致します。ただし、入力は変数に割り当てるのではなく破棄されます。 ワイルドカード パターンは、他のパターン内で、->
記号の右側の式で必要のない値のプレースホルダーとして使用されることがよくあります。 ワイルドカード パターンは、一致しない入力に一致するパターンの一覧の末尾でも頻繁に使用されます。 ワイルドカード パターンは、このトピックの多くのコード例で示されています。 1 つの例については、上記のコードを参照してください。
型の注釈が付けられたパターン
パターンには型注釈を含めることができます。 これらは他の型注釈と同様に動作し、他の型注釈と同様に推論をガイドします。 パターンの型注釈の周りには括弧が必要です。 次のコードは、型注釈を持つパターンを示しています。
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."
| _ -> ()
識別子が特定の派生型であるかどうかを確認するだけの場合は、次の例に示すように、パターンの as identifier
部分は必要ありません。
type A() = class end
type B() = inherit A()
type C() = inherit A()
let m (a: A) =
match a with
| :? B -> printfn "It's a B"
| :? C -> printfn "It's a C"
| _ -> ()
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()
F# 9 null 許容機能には、null パターンも推奨されます。
let len (str: string | null) =
match str with
| null -> -1
| s -> s.Length
同様に、パターンに関連する新しい専用の null 許容を使用できます。
let let str = // str is inferred to be `string | null`
match str with
| Null -> -1
| NonNull (s: string) -> s.Length
Nameof パターン
nameof
パターンは、その値が nameof
キーワードに続く式と等しい場合に、文字列と一致します。 例えば:
let f (str: string) =
match str with
| nameof str -> "It's 'str'!"
| _ -> "It is not 'str'!"
f "str" // matches
f "asdf" // does not match
名前を指定できる内容については、nameof
演算子を参照してください。
関連項目
.NET