Dela via


Mönstermatchning

Mönster är regler för att transformera indata. De används i hela F# för att jämföra data med en logisk struktur eller strukturer, dela upp data i komponenter eller extrahera information från data på olika sätt.

Kommentarer

Mönster används i många språkkonstruktioner, till exempel uttrycket match . De används när du bearbetar argument för funktioner i let bindningar, lambda-uttryck och i undantagshanterare som är associerade med try...with uttrycket. Mer information finns i Matcha uttryck, låt bindningar, Lambda-uttryck: fun Nyckelord och undantag: Uttryckettry...with.

I uttrycket är till exempel match mönstret vad som följer pipe-symbolen.

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

Varje mönster fungerar som en regel för att transformera indata på något sätt. match I uttrycket granskas varje mönster i sin tur för att se om indata är kompatibla med mönstret. Om en matchning hittas körs resultatuttrycket. Om en matchning inte hittas testas nästa mönsterregel. Det valfria när villkorsdelen förklaras i Matcha uttryck.

Mönster som stöds visas i följande tabell. Vid körningen testas indata mot vart och ett av följande mönster i den ordning som anges i tabellen, och mönster tillämpas rekursivt, från första till sista som de visas i koden och från vänster till höger för mönstren på varje rad.

Name Beskrivning Exempel
Konstant mönster Numeriska tecken, tecken eller strängliteraler, en uppräkningskonstant eller en definierad literalidentifierare 1.0, "test", , 30Color.Red
Mönster för identifierare Ett ärendevärde för en diskriminerad union, en undantagsetikett eller ett aktivt mönsterfall Some(x)

Failure(msg)
Variabelmönster identifierare a
as Mönster mönster som identifierare (a, b) as tuple1
OR-mönster pattern1 | pattern2 ([h] | [h; _])
AND-mönster pattern1 & pattern2 (a, b) & (_, "test")
Mönster för nackdelar identifier :: list-identifier h :: t
Listmönster [ pattern_1; ... ; pattern_n ] [ a; b; c ]
Matrismönster [| pattern_1; ..; pattern_n |] [| a; b; c |]
Parenteserat mönster ( mönster ) ( a )
Tuppelns mönster ( pattern_1, ... , pattern_n ) ( a, b )
Postmönster { identifier1 = pattern_1; ... ; = identifier_n pattern_n } { Name = name; }
Mönster för jokertecken _ _
Mönster tillsammans med typanteckning mönster : typ a : int
Typtestmönster :? type [ as identifier ] :? System.DateTime as dt
Null-mönster NULL null
Namnmönster nameof expr nameof str

Konstanta mönster

Konstanta mönster är numeriska, tecken- och strängliteraler, uppräkningskonstanter (med uppräkningstypnamnet inkluderat). Ett match uttryck som bara har konstanta mönster kan jämföras med en case-instruktion på andra språk. Indata jämförs med literalvärdet och mönstret matchar om värdena är lika. Typen av literal måste vara kompatibel med typen av indata.

I följande exempel visas användningen av literalmönster och använder även ett variabelmönster och ett OR-mönster.

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

Ett annat exempel på ett literalmönster är ett mönster baserat på uppräkningskonstanter. Du måste ange uppräkningstypens namn när du använder uppräkningskonstanter.

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

Identifierarmönster

Om mönstret är en sträng med tecken som utgör en giltig identifierare avgör formen på identifieraren hur mönstret matchas. Om identifieraren är längre än ett enskilt tecken och börjar med ett versalt tecken försöker kompilatorn matcha identifierarmönstret. Identifieraren för det här mönstret kan vara ett värde som har markerats med attributet Literal, ett ärende för diskriminerad union, en undantagsidentifierare eller ett aktivt mönsterfall. Om ingen matchande identifierare hittas misslyckas matchningen och nästa mönsterregel, variabelmönstret, jämförs med indata.

Diskriminerade unionsmönster kan vara enkla namngivna fall eller ha ett värde eller en tupplar som innehåller flera värden. Om det finns ett värde måste du ange en identifierare för värdet. När det gäller en tuppeln måste du ange ett tuppelns mönster med en identifierare för varje element i tuppeln eller en identifierare med ett fältnamn för ett eller flera namngivna union-fält. Se kodexemplen i det här avsnittet för exempel.

Typen option är en diskriminerad union som har två fall, Some och None. Det ena fallet (Some) har ett värde, men det andra (None) är bara ett namngivet ärende. Some Därför måste ha en variabel för det värde som är associerat med Some ärendet, men None måste visas av sig själv. I följande kod får variabeln var1 det värde som erhålls genom matchning till ärendet Some .

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

I följande exempel innehåller den PersonName diskriminerade unionen en blandning av strängar och tecken som representerar möjliga former av namn. Fallen med den diskriminerade unionen är FirstOnly, LastOnlyoch 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ör diskriminerade fackföreningar som har namngett fält använder du likhetstecknet (=) för att extrahera värdet för ett namngivet fält. Tänk dig till exempel en diskriminerad union med en förklaring som följande.

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

Du kan använda de namngivna fälten i ett mönstermatchningsuttryck på följande sätt.

let matchShape shape =
    match shape with
    | Rectangle(height = h) -> printfn $"Rectangle with length %f{h}"
    | Circle(r) -> printfn $"Circle with radius %f{r}"

Det är valfritt att använda det namngivna fältet, så i föregående exempel har båda Circle(r) och Circle(radius = r) samma effekt.

När du anger flera fält använder du semikolonet (;) som avgränsare.

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

Med aktiva mönster kan du definiera mer komplex anpassad mönstermatchning. Mer information om aktiva mönster finns i Aktiva mönster.

Det fall då identifieraren är ett undantag används i mönstermatchning i kontexten för undantagshanterare. Information om mönstermatchning vid undantagshantering finns i Undantag: Uttryckettry...with.

Variabelmönster

Variabelmönstret tilldelar värdet som matchas till ett variabelnamn, som sedan är tillgängligt för användning i körningsuttrycket till höger om symbolen -> . Endast ett variabelmönster matchar alla indata, men variabelmönster visas ofta i andra mönster, vilket gör att mer komplexa strukturer som tupplar och matriser kan delas upp i variabler.

I följande exempel visas ett variabelmönster i ett tupplar.

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)

som mönster

Mönstret as är ett mönster som har en as sats som läggs till. as Satsen binder det matchade värdet till ett namn som kan användas i körningsuttrycket för ett match uttryck, eller, om det här mönstret används i en let bindning, läggs namnet till som en bindning till det lokala omfånget.

I följande exempel används ett as mönster.

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

ELLER-mönster

OR-mönstret används när indata kan matcha flera mönster och du vill köra samma kod som ett resultat. Typerna av båda sidor av OR-mönstret måste vara kompatibla.

I följande exempel visas OR-mönstret.

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-mönster

AND-mönstret kräver att indata matchar två mönster. Typerna av båda sidor av AND-mönstret måste vara kompatibla.

Följande exempel visas som detectZeroTuple i avsnittet Tuppeln mönster senare i det här avsnittet, men här både var1 och var2 hämtas som värden med hjälp av AND-mönstret.

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)

Mönster för nackdelar

Cons-mönstret används för att dela upp en lista i det första elementet, huvudet och en lista som innehåller de återstående elementen, svansen.

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

Listmönster

Listmönstret gör att listor kan delas upp i ett antal element. Själva listmönstret kan endast matcha listor med ett visst antal element.

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

Matrismönster

Matrismönstret liknar listmönstret och kan användas för att dela upp matriser med en viss längd.

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

Parenteserat mönster

Parenteser kan grupperas runt mönster för att uppnå önskad associativitet. I följande exempel används parenteser för att styra associativitet mellan ett AND-mönster och ett cons-mönster.

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

Tuppelns mönster

Tuppelns mönster matchar indata i tuppelns form och gör att tuppeln kan delas upp i dess element genom att använda mönstermatchningsvariabler för varje position i tuppeln.

Följande exempel visar tuppelns mönster och använder även literalmönster, variabelmönster och jokerteckenmönstret.

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)

Postmönster

Postmönstret används för att dela upp poster för att extrahera fältens värden. Mönstret behöver inte referera till alla fält i posten. alla utelämnade fält deltar helt enkelt inte i matchning och extraheras inte.

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

Mönster för jokertecken

Jokertecknet representeras av understrecket (_) och matchar alla indata, precis som variabelmönstret, förutom att indata ignoreras i stället för att tilldelas till en variabel. Jokerteckenmönstret används ofta i andra mönster som platshållare för värden som inte behövs i uttrycket till höger om symbolen -> . Jokerteckenmönstret används också ofta i slutet av en lista med mönster för att matcha omatchade indata. Jokerteckenmönstret visas i många kodexempel i det här avsnittet. Se föregående kod för ett exempel.

Mönster som har typanteckningar

Mönster kan ha typanteckningar. Dessa fungerar som andra typanteckningar och guideinferens som andra typanteckningar. Parenteser krävs runt typanteckningar i mönster. Följande kod visar ett mönster som har en typanteckning.

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

Typtestmönster

Typtestmönstret används för att matcha indata mot en typ. Om indatatypen är en matchning till (eller en härledd typ av) den typ som anges i mönstret lyckas matchningen.

I följande exempel visas typtestmönstret.

open System.Windows.Forms

let RegisterControl(control:Control) =
    match control with
    | :? Button as button -> button.Text <- "Registered."
    | :? CheckBox as checkbox -> checkbox.Text <- "Registered."
    | _ -> ()

Om du bara kontrollerar om en identifierare är av en viss härledd typ behöver du inte den as identifier del av mönstret som visas i följande exempel:

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-mönster

Null-mönstret matchar null-värdet som kan visas när du arbetar med typer som tillåter ett null-värde. Null-mönster används ofta vid samverkan med .NET Framework-kod. Till exempel kan returvärdet för ett .NET API vara indata till ett match uttryck. Du kan styra programflödet baserat på om returvärdet är null och även på andra egenskaper för det returnerade värdet. Du kan använda null-mönstret för att förhindra att null-värden sprids till resten av programmet.

I följande exempel används null-mönstret och variabelmönstret.

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

Namnmönster

Mönstret nameof matchar en sträng när dess värde är lika med uttrycket som följer nyckelordet nameof . till exempel:

let f (str: string) =
    match str with
    | nameof str -> "It's 'str'!"
    | _ -> "It is not 'str'!"

f "str" // matches
f "asdf" // does not match

Se operatorn nameof för information om vad du kan ta ett namn på.

Se även