Teilen über


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.

Siehe auch