Mustervergleich
Muster sind Regeln zum Transformieren von Eingabedaten. Sie werden in F# verwendet, um Daten mit einer logischen Struktur oder Strukturen zu vergleichen, Daten in Bestandteile zu zerlegen oder Informationen aus Daten auf verschiedene Weise zu extrahieren.
Bemerkungen
Muster werden in vielen Sprachkonstrukten wie dem match
Ausdruck verwendet. Sie werden verwendet, wenn Sie Argumente für Funktionen in let
Bindungen, Lambda-Ausdrücken und in den Ausnahmehandlern verarbeiten, die dem try...with
Ausdruck zugeordnet sind. Weitere Informationen finden Sie unter Abgleichen von Ausdrücken, let-Bindungen, Lambdaausdrücke: Das fun
-Schlüsselwort und Ausnahmen: Der try...with
-Ausdruck.
Beispielsweise wird das Muster im match
-Ausdruck hinter dem Pipesymbol angegeben.
match expression with
| pattern [ when condition ] -> result-expression
...
Jedes Muster dient als Regel zum Transformieren von Eingaben in irgendeiner Weise. Im match
Ausdruck wird jedes Muster wiederum untersucht, um festzustellen, ob die Eingabedaten mit dem Muster kompatibel sind. Wenn eine Übereinstimmung gefunden wird, wird der Ergebnisausdruck ausgeführt. Wenn keine Übereinstimmung gefunden wird, wird die nächste Musterregel getestet. Der optionale Teil „when condition“ wird unter Abgleichen von Ausdrücken erläutert.
Unterstützte Muster werden in der folgenden Tabelle angezeigt. Zur Laufzeit wird die Eingabe anhand jedes der folgenden Muster in der in der Tabelle aufgeführten Reihenfolge überprüft. Die Muster werden rekursiv vom ersten bis zum letzten Muster im Code und von links nach rechts in den einzelnen Zeilen angewendet.
Name | Beschreibung | Beispiel |
---|---|---|
Konstantenmuster | Ein beliebiges numerisches Literal, Zeichenliteral oder Zeichenfolgenliteral, eine Enumerationskonstante oder ein definierter Literalbezeichner. | 1.0 , "test" , 30 , Color.Red |
Bezeichnermuster | Ein Fallwert einer diskriminierten Union, einer Ausnahmebezeichnung oder eines aktiven Musterfalls | Some(x) Failure(msg) |
Variablenmuster | Bezeichner | a |
as -Muster |
Muster als Bezeichner | (a, b) as tuple1 |
OR-Muster | Muster1 | Muster2 | ([h] | [h; _]) |
AND-Muster | Muster1 & Muster2 | (a, b) & (_, "test") |
Cons-Muster | Bezeichner :: list-identifier | h :: t |
Listenmuster | [ Muster_1; ... ; Muster_n ] | [ a; b; c ] |
Arraymuster | [| Muster_1; ..; Muster_n |] | [| a; b; c |] |
Muster in Klammern | ( Muster ) | ( a ) |
Tupelmuster | ( Muster_1, ... , Muster_n ) | ( a, b ) |
Aufzeichnungsmuster | { Bezeichner1 = Muster_1; ... ; Muster_n = Muster_n } | { Name = name; } |
Platzhaltermuster | _ | _ |
Muster zusammen mit Typanmerkung | Muster : Typ | a : int |
Typtestmuster | :? Typ [ as Bezeichner ] | :? System.DateTime as dt |
NULL-Muster | null | null |
Nameof-Muster | nameof expr | nameof str |
Konstante Muster
Konstantenmuster sind numerische Literale, Zeichenliterale und Zeichenfolgenliterale, Enumerationskonstanten (einschließlich Enumerationstypnamen). Ein match
-Ausdruck, der nur über Konstantenmuster verfügt, ist mit case-Anweisungen in anderen Sprachen vergleichbar. Die Eingabe wird mit dem Literalwert verglichen, und das Muster stimmt überein, wenn die Werte gleich sind. Der Typ des Literals muss mit dem Typ der Eingabe kompatibel sein.
Das folgende Beispiel veranschaulicht die Verwendung von Literalmustern und verwendet außerdem ein Variablenmuster und ein OR-Muster.
[<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
Ein weiteres Beispiel für ein Literalmuster ist ein Muster, das auf Enumerationskonstanten basiert. Sie müssen den Enumerationstypnamen angeben, wenn Sie Enumerationskonstanten verwenden.
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
Bezeichnermuster
Wenn es sich bei dem Muster um eine Zeichenfolge handelt, die einen gültigen Bezeichner bildet, bestimmt die Form des Bezeichners, wie das Muster abgeglichen wird. Wenn der Bezeichner länger als ein einzelnes Zeichen ist und mit einem Großbuchstaben beginnt, versucht der Compiler, eine Übereinstimmung mit dem Bezeichnermuster zu erstellen. Der Bezeichner für dieses Muster kann ein Wert sein, der mit dem Literal-Attribut, einem diskriminierten Union-Fall, einem Ausnahmebezeichner oder einem aktiven Musterfall gekennzeichnet ist. Wenn kein übereinstimmende Bezeichner gefunden wird, schlägt die Übereinstimmung fehl, und die nächste Musterregel, das Variablemuster, wird mit der Eingabe verglichen.
Unterscheidungs-Union-Muster können einfache benannte Fälle sein oder über einen Wert bzw. über ein mehrere Werte enthaltendes Tupel verfügen. Wenn ein Wert vorhanden ist, müssen Sie einen Bezeichner für den Wert angeben. Im Fall eines Tupels müssen Sie ein Tupelmuster mit einem Bezeichner für jedes Element des Tupels oder einen Bezeichner mit einem Feldnamen für eine oder mehrere benannte Union-Felder angeben. Beispiele finden Sie in den Codebeispielen in diesem Abschnitt.
Der option
-Typ ist eine Unterscheidungs-Union mit den beiden Fällen Some
und None
. Ein Fall (Some
) hat einen Wert, aber die andere (None
) ist nur ein benannter Fall. Daher muss Some
eine Variable für den Wert haben, der dem Fall Some
zugeordnet ist, aber None
muss selbst erscheinen. Im folgenden Code wird der Variablen var1
der Wert zugewiesen, der durch den Vergleich mit dem Fall Some
ermittelt wurde.
let printOption (data : int option) =
match data with
| Some var1 -> printfn "%d" var1
| None -> ()
Im folgenden Beispiel enthält die diskriminierte Union PersonName
eine Mischung aus Strings und Zeichen, die mögliche Formen von Namen darstellen. Die Fälle der Unterscheidungs-Union lauten FirstOnly
, LastOnly
und 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
Bei diskriminierten Vereinigungen mit benannten Feldern verwenden Sie das Gleichheitszeichen (=), um den Wert eines benannten Felds zu extrahieren. Ziehen Sie beispielsweise eine diskriminierte Vereinigung mit einer Deklaration wie der folgenden in Betracht.
type Shape =
| Rectangle of height : float * width : float
| Circle of radius : float
Sie können die benannten Felder in einem Musterabgleichsausdruck wie folgt verwenden.
let matchShape shape =
match shape with
| Rectangle(height = h) -> printfn $"Rectangle with length %f{h}"
| Circle(r) -> printfn $"Circle with radius %f{r}"
Die Verwendung des benannten Felds ist optional, sodass im vorherigen Beispiel sowohl Circle(r)
als auch Circle(radius = r)
denselben Effekt haben.
Wenn Sie mehrere Felder angeben, verwenden Sie das Semikolon (;) als Trennzeichen).
match shape with
| Rectangle(height = h; width = w) -> printfn $"Rectangle with height %f{h} and width %f{w}"
| _ -> ()
Mit aktiven Mustern können Sie komplexere benutzerdefinierte Musterabgleiche definieren. Weitere Informationen zu aktiven Mustern finden Sie unter Active Patterns.
Der Fall, in dem der Bezeichner eine Ausnahme ist, wird im Kontext von Ausnahmehandlern im Musterabgleich verwendet. Informationen zum Musterabgleich bei der Ausnahmebehandlung finden Sie unter Ausnahmen: Der try...with
-Ausdruck.
Variablenmuster
Das Variablenmuster weist den abgeglichenen Wert einem Variablennamen zu, der dann rechts neben dem ->
-Symbol im Ausführungsausdruck verfügbar ist. Ein variables Muster stimmt allein mit jeder Eingabe überein, aber Variablenmuster werden häufig in anderen Mustern angezeigt, sodass komplexere Strukturen wie Tupel und Arrays in Variablen dekompiliert werden können.
Im folgenden Beispiel wird ein Variablenmuster innerhalb eines Tupelmusters veranschaulicht.
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)
als Muster
Das as
Muster ist ein Muster, das eine as
Klausel angefügt hat. Die as
-Klausel bindet den übereinstimmenden Wert an einen Namen, der im Ausführungs-Ausdruck eines match
-Ausdrucks verwendet werden kann; oder, wenn dieses Muster in einer let
-Bindung verwendet wird, wird der Name als Bindung in den lokalen Bereich hinzugefügt.
Im folgenden Beispiel wird ein as
Muster verwendet.
let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1
OR-Muster
Das OR-Muster wird verwendet, wenn Eingabedaten mit mehreren Mustern übereinstimmen können und Sie denselben Code als Ergebnis ausführen möchten. Die Typen beider Seiten des OR-Musters müssen kompatibel sein.
Das OR-Muster wird im folgenden Beispiel veranschaulicht.
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-Muster
Das AND-Muster erfordert, dass die Eingabe zwei Muster erfüllt. Die Typen beider Seiten des AND-Musters müssen kompatibel sein.
Das folgende Beispiel ähnelt detectZeroTuple
im Abschnitt "Tupelmuster" weiter unten in diesem Thema gezeigt, aber hier werden sowohl var1
als auch var2
mithilfe des AND-Musters als Werte abgerufen.
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-Muster
Das Cons-Pattern wird verwendet, um eine Liste in ihr erstes Element, den Kopf, und eine Liste, die die restlichen Elemente, den Schwanz, enthält, zu zerlegen.
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
Listenmuster
Das Listenmuster ermöglicht die Dekompilierung von Listen in eine Reihe von Elementen. Das Listenmuster selbst kann nur mit Listen einer bestimmten Anzahl von Elementen übereinstimmen.
// 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 [ ] )
Arraymuster
Das Arraymuster ähnelt dem Listenmuster und kann verwendet werden, um Arrays einer bestimmten Länge zu dekompilieren.
// 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 [| |] )
In Klammern gesetztes Muster
Klammern können um Muster gruppiert werden, um die gewünschte Assoziivität zu erzielen. Im folgenden Beispiel werden Klammern verwendet, um die Zuordnung zwischen einem AND-Muster und einem Cons-Muster zu steuern.
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
Tupelmuster
Mithilfe des Tupelmusters werden Eingabe in Tupelform verglichen. Es ermöglicht das Zerlegen des Tupels in seine Bestandteile mithilfe von Musterabgleichsvariablen für die einzelnen Positionen im Tupel.
Das folgende Beispiel veranschaulicht das Tupelmuster und verwendet auch Literalmuster, Variablenmuster und das Wildcardmuster.
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)
Aufzeichnungsmuster
Das Datensatzmuster wird verwendet, um Datensätze zu dekompilieren, um die Werte von Feldern zu extrahieren. Das Muster muss nicht auf alle Felder des Datensatzes verweisen; Ausgelassene Felder nehmen nur nicht am Abgleich teil und werden nicht extrahiert.
// 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"
Platzhaltermuster
Das Wildcardmuster wird durch den Unterstrich (_
) dargestellt und entspricht jeder Eingabe, genau wie das Variablemuster, mit der Ausnahme, dass die Eingabe verworfen wird, anstatt einer Variablen zugewiesen zu werden. Das Platzhaltermuster wird häufig in anderen Mustern als Platzhalter für Werte verwendet, die im Ausdruck rechts neben dem ->
-Symbol nicht benötigt werden. Das Wildcardmuster wird auch häufig am Ende einer Liste von Mustern verwendet, um jeder nicht übereinstimmenden Eingabe zu entsprechen. Das Wildcardmuster wird in vielen Codebeispielen in diesem Thema veranschaulicht. Ein Beispiel finden Sie im vorherigen Code.
Muster mit Typannotationen
Muster können Typanmerkungen aufweisen. Diese verhalten sich wie andere Typanmerkungen und steuern Typrückschluss wie diese. Typanmerkungen in Mustern müssen in Klammern eingeschlossen werden. Im folgenden Code wird ein Muster veranschaulicht, das eine Typanmerkung aufweist.
let detect1 x =
match x with
| 1 -> printfn "Found a 1!"
| (var1 : int) -> printfn "%d" var1
detect1 0
detect1 1
Typtestmuster
Das Typtestmuster wird verwendet, um die Eingabe mit einem Typ abzugleichen. Wenn der Eingabetyp mit dem im Muster angegebenen Typ (oder einem abgeleiteten Typ davon) übereinstimmt, ist die Übereinstimmung erfolgreich.
Das folgende Beispiel veranschaulicht das Typtestmuster.
open System.Windows.Forms
let RegisterControl(control:Control) =
match control with
| :? Button as button -> button.Text <- "Registered."
| :? CheckBox as checkbox -> checkbox.Text <- "Registered."
| _ -> ()
Wenn Sie nur überprüfen, ob ein Bezeichner eines bestimmten abgeleiteten Typs ist, benötigen Sie nicht den as identifier
Teil des Musters, wie im folgenden Beispiel gezeigt:
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-Muster
Das Nullmuster entspricht dem Nullwert, der angezeigt werden kann, wenn Sie mit Typen arbeiten, die einen Nullwert zulassen. Nullmuster werden häufig bei der Interoperabilität mit .NET Framework-Code verwendet. Beispielsweise kann der Rückgabewert einer .NET-API die Eingabe für einen match
-Ausdruck sein. Sie können den Programmfluss basierend darauf steuern, ob der Rückgabewert null ist, und auch auf anderen Merkmalen des zurückgegebenen Werts. Sie können das Nullmuster verwenden, um zu verhindern, dass Nullwerte an den Rest Ihres Programms weitergegeben werden.
Im folgenden Beispiel wird das Nullmuster und das Variablemuster verwendet.
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()
Das NULL-Muster wird auch für die NULL-Zulässigkeit von F# 9 empfohlen:
let len (str: string | null) =
match str with
| null -> -1
| s -> s.Length
Ebenso können Sie neue dedizierte Muster für die NULL-Zulässigkeit verwenden:
let let str = // str is inferred to be `string | null`
match str with
| Null -> -1
| NonNull (s: string) -> s.Length
Nameof-Muster
Das nameof
Muster entspricht einer Zeichenfolge, wenn der Wert dem Ausdruck entspricht, der dem schlüsselwort nameof
folgt. Zum Beispiel:
let f (str: string) =
match str with
| nameof str -> "It's 'str'!"
| _ -> "It is not 'str'!"
f "str" // matches
f "asdf" // does not match
Informationen zu den Elementen, deren Namen Sie verwenden können, finden Sie unter dem nameof
-Operator.