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" , , 30 Color.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
, LastOnly
och 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å.