共用方式為


模式比對 (F#)

「模式」(Pattern) 是轉換輸入資料的規則。 在 F# 語言中模式用於比較具有邏輯結構的資料、將資料分解為構成部分,或是以各種方式從資料擷取資訊。

備註

模式可用於許多語言建構 (例如 match 運算式)。 當您處理 let 繫結、Lambda 運算式,以及與 try...with 運算式相關聯之例外處理常式中函式的引數時,都會使用模式。 如需詳細資訊,請參閱搜尋運算式 (F#)let 繫結 (F#)Lambda 運算式:fun 關鍵字 (F#)例外狀況:try...with 運算式 (F#)

例如,在 match 運算式中,pattern 是位於管道符號後面的內容。

match expression with

|pattern [ when condition ] -> result-expression

...

每個模式都是一種轉換規則,以某種方式對輸入資料進行轉換。 在 match 運算式中,會輪流檢查每個模式,確定輸入資料是否與模式相容。 如果找到符合的項目,則會執行結果運算式。 如果找不到符合的項目,則會測試下一個模式規則。 搜尋運算式 (F#)會說明選擇性 when condition 這個部分。

下表說明支援的模式。 在執行階段,會針對以下表格所列每個模式的順序測試輸入及遞迴套用模式。套用時,會從程式碼中第一個模式套用到最後一個模式,以及從左到右來套用每一行出現的模式。

名稱

描述

範例

常數模式

任何數值、字元或字串常值、列舉常數或已定義的常值識別項

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 )

Tuple 模式

( 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

識別項模式

如果模式是形成有效識別碼的字元字串,則識別碼的形式決定模式的比對方式。 如果識別項長度大於單一字元且開頭為大寫字元,編譯器會嘗試比對識別項模式。 此模式的識別項可以是以常值屬性、差別聯集、例外狀況識別項或現用模式案例所標記的值。 如果找不到符合的識別項,比對便失敗,下一個模式規則 (變數模式) 會與輸入比較。

已區分的聯集模式可以是簡單的具名案例、有單一值,或是包含多個值的 Tuple。 如果有一個值,您必須指定值的識別項,在 Tuple 的情況下,則必須為 Tuple 的每個項目提供具有識別項的 Tuple 模式。 如需範例,請參閱本節的程式碼範例。

option 型別是有 Some 和 None 兩個案例的已區分聯集。 一個案例 (Some) 有值,但另一個 (None) 只是具名案例。 因此,Some 必須有與 Some 案例關聯之值的變數,但是 None 必須單獨出現。 在下列程式碼中,比對 Some 案例所取得的值會提供給變數 var1。

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

變數模式

變數模式會將符合的值指派給變數名稱,接著此變數即可在 -> 符號右方的執行運算式中使用。 變數模式本身會比對任何輸入,但是變數模式通常會出現在其他模式中,因此可將更複雜的結構 (例如 Tuple 和陣列) 分解為變數。

下列範例將示範 Tuple 模式中的變數模式。

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 模式要求輸入應符合兩個模式。 AND 模式兩邊的型別必須相容。

下列範例類似於本主題稍後之 Tuple 模式一節中的 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 模式用於將清單分解為第一個項目 (即「頭」(Head)),以及包含其餘項目的清單 (即「尾」(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

清單模式

清單模式可讓清單分解為許多項目。 清單模式本身只能比對具有特定項目數目的清單。

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

Tuple 模式

Tuple 模式會比對 Tuple 形式的輸入,並且透過對 Tuple 中的每個位置使用模式比對變數,讓 Tuple 分解為其構成項目。

下列範例會示範 Tuple 模式的用法,並且會使用常值模式、變數模式和萬用字元模式。

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 模式常用於 F# 與 .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#)

作用中的模式 (F#)

其他資源

F# 語言參考