模式比對Pattern Matching

模式是轉換輸入資料的規則。Patterns are rules for transforming input data. 這兩種方法都F#是用來比較資料與邏輯結構或結構、將資料分解成組成元件, 或從資料中以各種方式來解壓縮資訊。They are used throughout the F# language to compare data with a logical structure or structures, decompose data into constituent parts, or extract information from data in various ways.

備註Remarks

模式用於許多語言結構, 例如match運算式。Patterns are used in many language constructs, such as the match expression. 當您要在let系結、lambda 運算式和try...with與運算式相關聯的例外狀況處理常式中處理函式的引數時, 會使用這些函數。They are used when you are processing arguments for functions in let bindings, lambda expressions, and in the exception handlers associated with the try...with expression. 如需詳細資訊, 請參閱Match 運算式let系結、 Lambda 運算式:fun 關鍵字和例外狀況:try...with運算式。For more information, see Match Expressions, let Bindings, Lambda Expressions: The fun Keyword, and Exceptions: The try...with Expression.

例如, 在match運算式中,模式就是管道符號後面的內容。For example, in the match expression, the pattern is what follows the pipe symbol.

match expression with
| pattern [ when condition ] -> result-expression
...

每個模式都是以某種方式轉換輸入的規則。Each pattern acts as a rule for transforming input in some way. match在運算式中, 會接著檢查每個模式, 以查看輸入資料是否與模式相容。In the match expression, each pattern is examined in turn to see if the input data is compatible with the pattern. 如果找到相符的, 則會執行結果運算式。If a match is found, the result expression is executed. 如果找不到相符的, 則會測試下一個模式規則。If a match is not found, the next pattern rule is tested. Match 運算式中說明了條件部分時的選擇性時機。The optional when condition part is explained in Match Expressions.

下表顯示支援的模式。Supported patterns are shown in the following table. 在執行時間, 系統會依照表格中所列的順序, 針對下列每個模式測試輸入, 並以遞迴方式套用模式, 從第一次到最後, 和出現在您的程式碼中, 以及從左至右套用到每一行上的模式。At run time, the input is tested against each of the following patterns in the order listed in the table, and patterns are applied recursively, from first to last as they appear in your code, and from left to right for the patterns on each line.

名稱Name 描述Description 範例Example
常數模式Constant pattern 任何數值、字元或字串常值、列舉常數或定義的常值識別碼Any numeric, character, or string literal, an enumeration constant, or a defined literal identifier 1.0, "test", 30, Color.Red1.0, "test", 30, Color.Red
識別碼模式Identifier pattern 區分聯集、例外狀況標籤或作用中模式案例的大小寫值A case value of a discriminated union, an exception label, or an active pattern case Some(x)

Failure(msg)
變數模式Variable pattern identifieridentifier a
aspatternas pattern 模式作為識別碼pattern as identifier (a, b) as tuple1
OR 模式OR pattern pattern1 | pattern2pattern1 | pattern2 ([h] | [h; _])
AND 模式AND pattern pattern1 & pattern2pattern1 & pattern2 (a, b) & (_, "test")
缺點模式Cons pattern identifier :: list-identifieridentifier :: list-identifier h :: t
清單模式List pattern [ pattern_1; ... ; pattern_n ][ pattern_1; ... ; pattern_n ] [ a; b; c ]
陣列模式Array pattern [| pattern_1; ...;pattern_n|][| pattern_1; ..; pattern_n |] [| a; b; c |]
以括弧括住的模式Parenthesized pattern (模式)( pattern ) ( a )
元組模式Tuple pattern ( pattern_1, ..., pattern_n )( pattern_1, ... , pattern_n ) ( a, b )
記錄模式Record pattern { identifier1 = pattern_1; ...;identifier_n = pattern_n }{ identifier1 = pattern_1; ... ; identifier_n = pattern_n } { Name = name; }
萬用字元模式Wildcard pattern _
搭配類型注釋的模式Pattern together with type annotation 模式:類型pattern : type a : int
類型測試模式Type test pattern :?:? 類型[as identifier ]type [ as identifier ] :? System.DateTime as dt
Null 模式Null pattern nullnull null

常數模式Constant Patterns

常數模式是數值、字元和字串常值、列舉常數 (包含列舉型別名稱)。Constant patterns are numeric, character, and string literals, enumeration constants (with the enumeration type name included). 只有常數模式的運算式可以與其他語言中的case語句進行比較。matchA match expression that has only constant patterns can be compared to a case statement in other languages. 輸入會與常值進行比較, 而如果值相等, 則會符合模式。The input is compared with the literal value and the pattern matches if the values are equal. 常值的型別必須與輸入的型別相容。The type of the literal must be compatible with the type of the input.

下列範例示範如何使用常值模式, 而且也會使用變數模式和或模式。The following example demonstrates the use of literal patterns, and also uses a variable pattern and an OR pattern.

[<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

常值模式的另一個範例是以列舉常數為基礎的模式。Another example of a literal pattern is a pattern based on enumeration constants. 當您使用列舉常數時, 必須指定列舉類型名稱。You must specify the enumeration type name when you use enumeration constants.

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

識別碼模式Identifier Patterns

如果模式是形成有效識別碼的字元字串, 則識別碼的格式會決定模式的比對方式。If the pattern is a string of characters that forms a valid identifier, the form of the identifier determines how the pattern is matched. 如果識別碼長度超過單一字元, 並以大寫字元開頭, 則編譯器會嘗試對識別碼模式進行比對。If the identifier is longer than a single character and starts with an uppercase character, the compiler tries to make a match to the identifier pattern. 此模式的識別碼可以是以 Literal 屬性、區分聯集大小寫、例外狀況識別碼或作用中模式案例標記的值。The identifier for this pattern could be a value marked with the Literal attribute, a discriminated union case, an exception identifier, or an active pattern case. 如果找不到相符的識別碼, 比對會失敗, 而下一個模式規則 (變數模式) 則會與輸入進行比較。If no matching identifier is found, the match fails and the next pattern rule, the variable pattern, is compared to the input.

區分聯集模式可以是簡單的命名案例, 或者可以有值, 或包含多個值的元組。Discriminated union patterns can be simple named cases or they can have a value, or a tuple containing multiple values. 如果有值, 您必須指定值的識別碼。If there is a value, you must specify an identifier for the value. 在元組的情況下, 您必須為元組的每個專案提供一個識別碼, 或為一個或多個命名聯集欄位指定具有功能變數名稱的識別碼。In the case of a tuple, you must supply a tuple pattern with an identifier for each element of the tuple or an identifier with a field name for one or more named union fields. 如需範例, 請參閱本節中的程式碼範例。See the code examples in this section for examples.

option類型是有兩個Some案例的區分聯集: None和。The option type is a discriminated union that has two cases, Some and None. 其中一個案例Some() 具有值, 但另一個 (None) 只是一個名為的大小寫。One case (Some) has a value, but the other (None) is just a named case. 因此, Some Some與案例相關聯的值需要有變數, 但None必須單獨顯示。Therefore, Some needs to have a variable for the value associated with the Some case, but None must appear by itself. 在下列程式碼中, 會var1提供變數比Some對案例所取得的值。In the following code, the variable var1 is given the value that is obtained by matching to the Some case.

let printOption (data : int option) =
    match data with
    | Some var1  -> printfn "%d" var1
    | None -> ()

在下列範例中, PersonName 「區分聯集」包含字串和字元的混合, 表示可能的名稱形式。In the following example, the PersonName discriminated union contains a mixture of strings and characters that represent possible forms of names. 區分聯集的案例為FirstOnlyLastOnlyFirstLastThe cases of the discriminated union are FirstOnly, LastOnly, and 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

針對具有命名欄位的區分等位, 您可以使用等號 (=) 來解壓縮已命名欄位的值。For discriminated unions that have named fields, you use the equals sign (=) to extract the value of a named field. 例如, 請考慮具有類似下列宣告的區分聯集。For example, consider a discriminated union with a declaration like the following.

type Shape =
    | Rectangle of height : float * width : float
    | Circle of radius : float

您可以使用模式比對運算式中的已命名欄位, 如下所示。You can use the named fields in a pattern matching expression as follows.

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)都具有相同的效果。The use of the named field is optional, so in the previous example, both Circle(r) and Circle(radius = r) have the same effect.

當您指定多個欄位時, 請使用分號 (;)作為分隔符號。When you specify multiple fields, use the semicolon (;) as a separator.

match shape with
| Rectangle(height = h; width = w) -> printfn "Rectangle with height %f and width %f" h w
| _ -> ()

現用模式可讓您定義更複雜的自訂模式比對。Active patterns enable you to define more complex custom pattern matching. 如需現用模式的詳細資訊, 請參閱現用模式For more information about active patterns, see Active Patterns.

在例外狀況處理常式內容中的模式比對中, 會使用識別碼為例外狀況的情況。The case in which the identifier is an exception is used in pattern matching in the context of exception handlers. 如需例外狀況處理中的模式比對的詳細資訊, 請參閱例外狀況:try...with運算式。For information about pattern matching in exception handling, see Exceptions: The try...with Expression.

變數模式Variable Patterns

變數模式會指派符合變數名稱的值, 然後可以在->符號右邊的執行運算式中使用。The variable pattern assigns the value being matched to a variable name, which is then available for use in the execution expression to the right of the -> symbol. 變數模式本身會符合任何輸入, 但變數模式通常會出現在其他模式中, 因此可讓更複雜的結構 (例如元組和陣列) 分解為變數。A variable pattern alone matches any input, but variable patterns often appear within other patterns, therefore enabling more complex structures such as tuples and arrays to be decomposed into variables.

下列範例示範在元組模式內的變數模式。The following example demonstrates a variable pattern within a tuple pattern.

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 Pattern

模式是一個as已附加子句的模式。 asThe as pattern is a pattern that has an as clause appended to it. 子句會將相符的值系結至可在match運算式的執行運算式中使用的名稱, 或者, 如果在系結中let使用此模式, 則會將名稱當做系結加入至本機範圍。 asThe as clause binds the matched value to a name that can be used in the execution expression of a match expression, or, in the case where this pattern is used in a let binding, the name is added as a binding to the local scope.

下列範例會使用as模式。The following example uses an as pattern.

let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1

OR 模式OR Pattern

當輸入資料可以符合多個模式, 而您想要執行與結果相同的程式碼時, 就會使用或模式。The OR pattern is used when input data can match multiple patterns, and you want to execute the same code as a result. 或模式的兩邊類型都必須相容。The types of both sides of the OR pattern must be compatible.

下列範例示範或模式。The following example demonstrates the OR pattern.

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 Pattern

和模式需要輸入符合兩個模式。The AND pattern requires that the input match two patterns. 和模式的兩邊類型都必須相容。The types of both sides of the AND pattern must be compatible.

下列範例如detectZeroTuple本主題稍後的「元組模式」一節所示var1 , 但在此, 我們會使用和模式, 將和var2都當做值來取得。The following example is like detectZeroTuple shown in the Tuple Pattern section later in this topic, but here both var1 and var2 are obtained as values by using the AND pattern.

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 Pattern

缺點模式是用來將清單分解成第一個專案、標頭, 以及包含其餘元素的清單 (結尾)。The cons pattern is used to decompose a list into the first element, the head, and a list that contains the remaining elements, the tail.

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

清單模式List Pattern

清單模式可讓清單分解成數個元素。The list pattern enables lists to be decomposed into a number of elements. 清單模式本身只能符合特定專案數目的清單。The list pattern itself can match only lists of a specific number of elements.

// 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 [ ] )

陣列模式Array Pattern

陣列模式類似于清單模式, 可以用來分解特定長度的陣列。The array pattern resembles the list pattern and can be used to decompose arrays of a specific length.

// 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 [| |] )

以括弧括住的模式Parenthesized Pattern

括弧可以根據模式分組, 以達到所需的關聯性。Parentheses can be grouped around patterns to achieve the desired associativity. 在下列範例中, 括弧是用來控制 AND 模式與缺點模式之間的關聯性。In the following example, parentheses are used to control associativity between an AND pattern and a cons pattern.

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

元組模式Tuple Pattern

元組模式會比對元組形式的輸入, 並透過針對元組中的每個位置使用模式比對變數, 讓元組分解成其組成元素。The tuple pattern matches input in tuple form and enables the tuple to be decomposed into its constituent elements by using pattern matching variables for each position in the tuple.

下列範例示範元組模式, 同時使用常值模式、變數模式和萬用字元模式。The following example demonstrates the tuple pattern and also uses literal patterns, variable patterns, and the wildcard pattern.

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)

記錄模式Record Pattern

記錄模式可用來分解記錄, 以將欄位的值解壓縮。The record pattern is used to decompose records to extract the values of fields. 模式不需要參考記錄的所有欄位;任何省略的欄位都不會參與比對, 也不會被解壓縮。The pattern does not have to reference all fields of the record; any omitted fields just do not participate in matching and are not extracted.

// 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"

萬用字元模式Wildcard Pattern

萬用字元模式是以底線 (_) 字元表示並比對任何輸入, 就像變數模式一樣, 不同的是會捨棄輸入, 而不是指派給變數。The wildcard pattern is represented by the underscore (_) character and matches any input, just like the variable pattern, except that the input is discarded instead of assigned to a variable. 萬用字元模式通常用於其他模式中, 做為->符號右邊運算式中不需要的值預留位置。The wildcard pattern is often used within other patterns as a placeholder for values that are not needed in the expression to the right of the -> symbol. 萬用字元模式也經常用於模式清單的結尾, 以符合任何不相符的輸入。The wildcard pattern is also frequently used at the end of a list of patterns to match any unmatched input. 本主題的許多程式碼範例會示範萬用字元模式。The wildcard pattern is demonstrated in many code examples in this topic. 如需其中一個範例, 請參閱上述程式碼。See the preceding code for one example.

具有類型注釋的模式Patterns That Have Type Annotations

模式可以具有類型注釋。Patterns can have type annotations. 這些行為與其他類型注釋和指南推斷類似, 如同其他類型注釋。These behave like other type annotations and guide inference like other type annotations. 模式中的類型注釋前後需要括弧。Parentheses are required around type annotations in patterns. 下列程式碼顯示具有類型注釋的模式。The following code shows a pattern that has a type annotation.

let detect1 x =
    match x with
    | 1 -> printfn "Found a 1!"
    | (var1 : int) -> printfn "%d" var1
detect1 0
detect1 1

類型測試模式Type Test Pattern

類型測試模式是用來比對輸入與類型。The type test pattern is used to match the input against a type. 如果輸入類型與模式中指定的類型相符 (或衍生的類型), 則比對成功。If the input type is a match to (or a derived type of) the type specified in the pattern, the match succeeds.

下列範例示範型別測試模式。The following example demonstrates the type test pattern.

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 Pattern

Null 模式符合當您使用允許 null 值的類型時, 可能會出現的 null 值。The null pattern matches the null value that can appear when you are working with types that allow a null value. 當與 .NET Framework 的程式碼互通時, 經常會使用 Null 模式。Null patterns are frequently used when interoperating with .NET Framework code. 例如, .net API 的傳回值可能是match運算式的輸入。For example, the return value of a .NET API might be the input to a match expression. 您可以根據傳回值是否為 null, 以及所傳回值的其他特性, 來控制程式流程。You can control program flow based on whether the return value is null, and also on other characteristics of the returned value. 您可以使用 null 模式來防止 null 值傳播到程式的其餘部分。You can use the null pattern to prevent null values from propagating to the rest of your program.

下列範例會使用 null 模式和變數模式。The following example uses the null pattern and the variable pattern.

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()

另請參閱See also