Mustervergleich (F#)

Muster sind Regeln zum Transformieren von Eingabedaten. Sie werden in F# stets verwendet, um Daten mit logischen Strukturen zu vergleichen, Daten in einzelne Bestandteile zu zerlegen oder auf unterschiedliche Weise Informationen aus Daten zu extrahieren.

Hinweise

Muster werden in vielen Sprachkonstrukten verwendet, z. B. im match-Ausdruck. Sie werden verwendet, wenn Argumente für Funktionen in let-Bindungen oder Lambda-Ausdrücken verarbeitet werden, sowie in den dem try...with-Ausdruck zugeordneten Ausnahmehandlern. Weitere Informationen finden Sie unter Vergleichsausdrücke (F#), let-Bindungen (F#), Lambda-Ausdrücke: Das fun-Schlüsselwort (F#) und Ausnahmen: Der try...with-Ausdruck (F#).

Beispielsweise wird im match-Ausdruck das pattern hinter dem Pipesymbol angegeben.

match expression with

| pattern [ when condition ] -> result-expression

...

Jedes Muster fungiert als Regel zum Transformieren von Eingaben. Im match-Ausdruck wird jedes Muster einzeln untersucht, um zu ermitteln, 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 in Vergleichsausdrücke (F#) erläutert.

In der folgenden Tabelle werden unterstützte Muster aufgeführt. 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

Der Wert eines Falls einer Unterscheidungs-Union, eine Ausnahmebezeichnung oder ein Fall eines aktiven Musters.

Some(x)

Failure(msg)

Variablenmuster

identifier

a

as-Muster

Muster as Bezeichner

(a, b) as tuple1

OR-Muster

Muster1 | Muster2

([h] | [h; _])

AND-Muster

Muster1 & Muster2

(a, b) & (_, "test")

Cons-Muster

identifier :: list-identifier

h :: t

Listenmuster

[ Muster_1; ... ; Muster_n ]

[ a; b; c ]

Arraymuster

[| Muster_1;..; Muster_n ]

[| a; b; c |]

Muster in Klammern

( pattern )

( a )

Tupelmuster

( Muster_1, ... , Muster_n )

( a, b )

Datensatzmuster

{ Bezeichner1 = Muster_1; ... ; Bezeichner_n = Muster_n }

{ Name = name; }

Platzhaltermuster

_

_

Muster zusammen mit Typanmerkung

pattern : type

a : int

Typtestmuster

:? type [ as identifier ]

:? System.DateTime as dt

NULL-Muster

null

null

Konstantenmuster

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.

Im folgenden Beispiel wird die Verwendung von Literalmustern veranschaulicht, und es werden außerdem ein Variablenmuster und ein OR-Muster verwendet.

[<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 auf Enumerationskonstanten basierendes Muster. Wenn Sie Enumerationskonstanten verwenden, müssen Sie den Enumerationstypnamen angeben.

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 das Muster eine Zeichenfolge ist, die einen gültigen Bezeichner bildet, bestimmt die Form des Bezeichners, wie das Muster verglichen 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 ermitteln. Der Bezeichner für dieses Muster kann ein Wert sein, der mit dem Literal-Attribut, einem Unterscheidungs-Unions-Fall, einem Ausnahmebezeichner oder einem Fall eines aktiven Musters markiert wurde. Wenn kein übereinstimmender Bezeichner gefunden wird, schlägt der Vergleich fehl, und die nächste Musterregel, das Variablenmuster, 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, und im Fall eines Tupels müssen Sie ein Tupelmuster mit einem Bezeichner für jedes Element des Tupels angeben. Entsprechende Beispiele finden Sie in den Codebeispielen dieses Abschnitts.

Der option-Typ ist eine Unterscheidungs-Union mit den beiden Fällen Some und None. Ein Fall (Some) verfügt über einen Wert, der andere Fall (None) ist jedoch lediglich ein benannter Fall. Daher muss Some über eine Variable für den dem Fall Some zugeordneten Wert verfügen, None muss jedoch als None angegeben werden. 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 Unterscheidungs-Union PersonName eine Kombination von Zeichenfolgen 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

Mit aktiven Mustern können Sie komplexere benutzerdefinierte Mustervergleiche definieren. Weitere Informationen über aktive Muster finden Sie unter Aktive Muster (F#).

Der Fall, in dem der Bezeichner eine Ausnahme ist, wird beim Mustervergleich im Kontext von Ausnahmehandlern verwendet. Informationen über Mustervergleich bei der Ausnahmebehandlung finden Sie unter Ausnahmen: Der try...with-Ausdruck (F#).

Variablenmuster

Im Variablenmuster wird dem zu vergleichenden Wert ein Variablenname zugewiesen, der dann im Ausführungsausdruck auf der rechten Seite des Symbols -> verwendet werden kann. Ein Variablenmuster an sich stimmt mit jeder Eingabe überein, jedoch sind Variablenmuster häufig in anderen Mustern enthalten und ermöglichen somit komplexere Strukturen, z. B. Tupel und Arrays, die in Variablen zerlegt werden sollen.

Im folgenden Beispiel wird ein Variablenmuster in einem Tupelmuster 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)

as-Muster

Das as-Muster ist ein Muster, an das eine as-Klausel angefügt ist. Die as-Klausel bindet den übereinstimmenden Wert an einen Namen, der im Ausführungsausdruck eines match-Ausdrucks verwendet werden kann. Falls das Muster in einer let-Bindung verwendet wird, wird stattdessen der Name dem lokalen Bereich als Bindung 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 als Ergebnis der gleiche Code ausgeführt werden soll. 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 mit zwei Mustern übereinstimmt. Die Typen beider Seiten des AND-Musters müssen kompatibel sein.

Das folgende Beispiel entspricht dem im Abschnitt Tupelmuster weiter unten in diesem Thema gezeigten detectZeroTuple, jedoch werden hier mit dem AND-Muster var1 und var2 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

Mit dem Cons-Muster wird eine Liste in das erste Element, den Kopf, und eine Liste, die aus den restlichen Elementen besteht, das Ende, zerlegt.

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

Mit dem Listenmuster können Listen in eine Reihe von Elementen zerlegt werden. Das Listenmuster selbst darf 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 es kann zum Zerlegen von Arrays einer bestimmten Länge verwendet werden.

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

Muster in Klammern

Muster können in Klammern eingeschlossen werden, um die gewünschte Assoziativität zu erreichen. Im folgenden Beispiel werden Klammern verwendet, um die Assoziativität 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 Mustervergleichsvariablen für die einzelnen Positionen im Tupel.

Im folgenden Beispiel wird die Verwendung des Tupelmusters veranschaulicht, und es werden außerdem Literalmuster, Variablenmuster und das Platzhaltermuster verwendet.

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)

Datensatzmuster

Mit dem Datensatzmuster werden Datensätze zerlegt, um die Werte von Feldern zu extrahieren. Das Muster muss nicht auf alle Felder des Datensatzes verweisen. Weggelassene Felder werden nicht verglichen und 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 Platzhaltermuster stimmt wie das Variablenmuster mit jeder Eingabe überein, jedoch wird die Eingabe nicht einer Variablen zugewiesen, sondern verworfen. Das Platzhaltermuster wird oft innerhalb anderer Muster als Platzhalter für Werte verwendet, die im Ausdruck auf der rechten Seite des Symbols -> nicht benötigt werden. Das Platzhaltermuster wird außerdem häufig am Ende einer Liste von Mustern als Übereinstimmung für jede nicht übereinstimmende Eingabe verwendet. Das Platzhaltermuster wird in vielen Codebeispielen dieses Themas veranschaulicht. Ein Beispiel finden Sie im vorangehenden Code.

Muster mit Typanmerkungen

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 anhand eines Typs zu vergleichen. Wenn der Eingabetyp mit dem im Muster angegebenen Typ oder einem von diesem abgeleiteten Typ übereinstimmt, ist der Vergleich erfolgreich.

Das Typtestmuster wird im folgenden Beispiel veranschaulicht.

open System.Windows.Forms

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

NULL-Muster

Das NULL-Muster wird mit dem NULL-Wert verglichen, der beim Arbeiten mit Typen, die NULL-Werte zulassen, vorhanden sein kann. NULL-Muster werden häufig bei der Interoperation mit .NET Framework-Code verwendet. Beispielsweise kann der Rückgabewert einer .NET-API die Eingabe für einen match-Ausdruck sein. Die Steuerung des Programmablaufs kann von Eigenschaften des Rückgabewerts abhängig gemacht werden, beispielsweise davon, ob der Rückgabewert NULL ist. Mithilfe des NULL-Musters können Sie verhindern, dass NULL-Werte an den Rest des Programms weitergegeben werden.

Im folgenden Beispiel werden das NULL-Muster und das Variablenmuster 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()

Siehe auch

Referenz

Vergleichsausdrücke (F#)

Aktive Muster (F#)

Weitere Ressourcen

F#-Sprachreferenz