Delen via


Patroonherkenning

Patronen zijn regels voor het transformeren van invoergegevens. Ze worden in F# gebruikt om gegevens op verschillende manieren te vergelijken met een logische structuur of structuren, gegevens op te delen in samenstellende delen of gegevens op verschillende manieren uit gegevens te extraheren.

Opmerkingen

Patronen worden gebruikt in veel taalconstructies, zoals de match expressie. Ze worden gebruikt wanneer u argumenten verwerkt voor functies in let bindingen, lambda-expressies en in de uitzonderingshandlers die zijn gekoppeld aan de try...with-expressie. Voor meer informatie, zie Match-expressies, let-bindingen, Lambda-expressies: het fun trefwoorden Uitzonderingen: de try...with expressie.

In de match-expressie volgt het patroon bijvoorbeeld het pijpsymbool.

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

Elk patroon fungeert als een regel voor het transformeren van invoer op een of andere manier. In de match expressie wordt elk patroon op zijn beurt onderzocht om te zien of de invoergegevens compatibel zijn met het patroon. Als er een overeenkomst wordt gevonden, wordt de resultaatexpressie uitgevoerd. Als er geen overeenkomst wordt gevonden, wordt de volgende patroonregel getest. De optionele "wanneer voorwaarde" wordt uitgelegd in Match-uitdrukkingen.

Ondersteunde patronen worden weergegeven in de volgende tabel. Tijdens de uitvoering wordt de invoer getest op elk van de volgende patronen in de volgorde die in de tabel wordt vermeld en worden patronen recursief toegepast, van eerste tot laatste, zoals ze in uw code worden weergegeven, en van links naar rechts voor de patronen op elke regel.

Naam Beschrijving Voorbeeld
Constante patroon Elke letterlijke numerieke, teken- of tekenreeks, een opsommingsconstante of een gedefinieerde letterlijke id 1.0, "test", 30, Color.Red
Id-patroon Een casewaarde van een gediscrimineerde samenvoeging, een uitzonderingslabel of een actief patroon Some(x)

Failure(msg)
Variabel patroon identificatie a
as patroon patroon als identificatie (a, b) as tuple1
OR-patroon patroon1 | patroon2 ([h] | [h; _])
AND-patroon patroon1 & patroon2 (a, b) & (_, "test")
Nadelen patroon kenmerk :: lijstkenmerk h :: t
Lijstpatroon [ pattern_1; ... ; pattern_n ] [ a; b; c ]
Matrixpatroon [| pattern_1; ..; pattern_n |] [| a; b; c |]
Patroon tussen haakjes ( patroon ) ( a )
Tuple-patroon ( pattern_1, ... , pattern_n ) ( a, b )
Opnamepatroon { identifier1 = pattern_1; ... ; identifier_n = pattern_n } { Name = name; }
Jokertekenpatroon _ _
Patroon samen met typeaantekening patroon: type a : int
Typetestpatroon :? type [ als identificator ] :? System.DateTime as dt
Null-patroon nul null
Naam van patroon naam van expr- nameof str

Constante patronen

Constante patronen zijn numerieke, teken- en tekenreeks letterlijke waarden, opsommingsconstanten (waarbij de naam van het opsommingstype is opgenomen). Een match-expressie met alleen constante patronen kan worden vergeleken met een case-instructie in andere talen. De invoer wordt vergeleken met de letterlijke waarde en het patroon komt overeen als de waarden gelijk zijn. Het type letterlijk moet compatibel zijn met het type invoer.

In het volgende voorbeeld ziet u het gebruik van letterlijke patronen en wordt ook een variabel patroon en een OR-patroon gebruikt.

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

Een ander voorbeeld van een letterlijk patroon is een patroon op basis van opsommingsconstanten. U moet de naam van het opsommingstype opgeven wanneer u opsommingsconstanten gebruikt.

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

Id-patronen

Als het patroon een tekenreeks is die een geldige identificator vormt, bepaalt de vorm van de identificator hoe het patroon wordt gematcht. Als de id langer is dan één teken en begint met een hoofdletter, probeert de compiler een overeenkomst te maken met het id-patroon. De identificatie voor dit patroon kan een waarde zijn die is gemarkeerd met het Literal-attribuut, een case van een gediscrimineerde unie, een uitzonderingsidentificatie of een actieve patrooncase. Als er geen overeenkomende id wordt gevonden, mislukt de overeenkomst en wordt de volgende patroonregel, het variabelepatroon, vergeleken met de invoer.

Gediscrimineerde samenvoegpatronen kunnen eenvoudige benoemde gevallen zijn of ze kunnen een waarde hebben, of een tuple met meerdere waarden. Als er een waarde is, moet u een id voor de waarde opgeven. In het geval van een tuple moet u een tuple-patroon opgeven met een id voor elk element van de tuple of een id met een veldnaam voor een of meer benoemde samenvoegvelden. Zie de codevoorbeelden in deze sectie voor voorbeelden.

Het option type is een gediscrimineerde vereniging met twee gevallen, Some en None. De ene case (Some) heeft een waarde, maar de andere (None) is slechts een benoemde case. Daarom moet Some een variabele hebben voor de waarde die is gekoppeld aan het Some geval, maar None moet op zichzelf staan. In de volgende code krijgt de variabele var1 de waarde die wordt verkregen door overeen te komen met het Some geval.

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

In het volgende voorbeeld bevat de PersonName gediscrimineerde samenvoeging een combinatie van tekenreeksen en tekens die mogelijke vormen van namen vertegenwoordigen. De gevallen van de gediscrimineerde unie zijn FirstOnly, LastOnlyen 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

Voor gediscrimineerde unies die benoemde velden hebben, gebruikt u het gelijkteken (=) om de waarde van een benoemd veld te extraheren. Denk bijvoorbeeld aan een gediscrimineerde vereniging met een verklaring zoals hieronder.

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

U kunt de benoemde velden als volgt gebruiken in een patroonkoppelingsexpressie.

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

Het gebruik van het benoemde veld is optioneel, dus in het vorige voorbeeld hebben zowel Circle(r) als Circle(radius = r) hetzelfde effect.

Wanneer u meerdere velden opgeeft, gebruikt u de puntkomma (;) als scheidingsteken.

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

Met actieve patronen kunt u complexere aangepaste patroonkoppelingen definiëren. Zie Actieve patronenvoor meer informatie over actieve patronen.

Het geval waarin de id een uitzondering is, wordt gebruikt in patroonkoppeling in de context van uitzonderingshandlers. Zie voor meer informatie over patroonvergelijking in uitzonderingsafhandeling Uitzonderingen: de try...with Expressie.

Variabele patronen

Het variabelepatroon wijst de waarde toe die overeenkomt met een naam van een variabele, die vervolgens beschikbaar is voor gebruik in de uitvoeringsexpressie rechts van het -> symbool. Een variabele patroon komt alleen overeen met elke invoer, maar variabele patronen verschijnen vaak in andere patronen, waardoor complexere structuren, zoals tuples en matrices, in variabelen kunnen worden opgesplitst.

In het volgende voorbeeld ziet u een variabel patroon binnen een tuple-patroon.

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 patroon

Het as-patroon is een patroon met een as component eraan toegevoegd. De as-component verbindt de overeenkomende waarde met een naam die kan worden gebruikt in de uitvoeringsexpressie van een match-expressie, of, in het geval dat dit patroon wordt gebruikt in een let binding, wordt de naam toegevoegd als een binding aan het lokale bereik.

In het volgende voorbeeld wordt een as patroon gebruikt.

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

OR-patroon

Het OR-patroon wordt gebruikt wanneer invoergegevens overeenkomen met meerdere patronen en u dezelfde code als een resultaat wilt uitvoeren. De typen van beide zijden van het OR-patroon moeten compatibel zijn.

In het volgende voorbeeld ziet u het OR-patroon.

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

Het AND-patroon vereist dat de invoer overeenkomt met twee patronen. De typen van de beide zijden van het AND-patroon moeten compatibel zijn.

Het volgende voorbeeld lijkt op detectZeroTuple weergegeven in de sectie Tuple-patroon verderop in dit onderwerp, maar hier worden zowel var1 als var2 verkregen als waarden met behulp van het AND-patroon.

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)

Nadelen patroon

Het nadelenpatroon wordt gebruikt om een lijst te ontleden in het eerste element, de kopen een lijst met de resterende elementen, de tail.

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

Lijstpatroon

Met het lijstpatroon kunnen lijsten worden opgesplitst in een aantal elementen. Het lijstpatroon zelf kan alleen overeenkomen met lijsten met een specifiek aantal elementen.

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

Matrixpatroon

Het matrixpatroon lijkt op het lijstpatroon en kan worden gebruikt om matrices van een specifieke lengte te decomposeen.

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

Haakjes patroon

Haakjes kunnen rond patronen worden gegroepeerd om de gewenste associativiteit te bereiken. In het volgende voorbeeld worden haakjes gebruikt om associativiteit tussen een AND-patroon en een nadelenpatroon te beheren.

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

Tuple-patroon

Het tuplepatroon komt overeen met invoer in tuplevorm en stelt de tuple in staat om uit te splitsen in zijn samenstellende elementen door patroonvariabelen te gebruiken voor elke positie in de tuple.

In het volgende voorbeeld ziet u het tuple-patroon en worden ook letterlijke patronen, variabele patronen en het jokertekenpatroon gebruikt.

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)

Opnamepatroon

Het recordpatroon wordt gebruikt om records te decomponeren en de waarden van velden te extraheren. Het patroon hoeft niet naar alle velden van de record te verwijzen; eventuele weggelaten velden nemen alleen niet deel aan overeenkomende velden en worden niet geëxtraheerd.

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

Jokertekenpatroon

Het jokerteken wordt vertegenwoordigd door het onderstrepingsteken (_) en komt overeen met alle invoer, net als het variabelepatroon, behalve dat de invoer wordt verwijderd in plaats van toegewezen aan een variabele. Het jokertekenpatroon wordt vaak gebruikt in andere patronen als tijdelijke aanduiding voor waarden die niet nodig zijn in de expressie rechts van het -> symbool. Het wildcardpatroon wordt ook vaak aan het einde van een lijst met patronen gebruikt om elke niet-overeenkomende invoer te matchen. Het jokertekenpatroon wordt in veel codevoorbeelden in dit onderwerp gedemonstreerd. Zie de voorgaande code voor één voorbeeld.

Patronen met typeaantekeningen

Patronen kunnen typeaantekeningen hebben. Deze gedragen zich als andere typeaantekeningen en sturen afleiding zoals andere typeaantekeningen. Haakjes zijn vereist rond typeaantekeningen in patronen. De volgende code toont een patroon met een typeaantekening.

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

Typ testpatroon

Het typetestpatroon wordt gebruikt om de invoer te vergelijken met een type. Als het invoertype overeenkomt met (of een afgeleid type) van het type dat is opgegeven in het patroon, slaagt de overeenkomst.

In het volgende voorbeeld ziet u het typetestpatroon.

open System.Windows.Forms

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

Als u alleen controleert of een id van een bepaald afgeleid type is, hebt u het as identifier deel van het patroon niet nodig, zoals wordt weergegeven in het volgende voorbeeld:

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

Nulpatroon

Het null-patroon komt overeen met de null-waarde die kan worden weergegeven wanneer u met typen werkt die een null-waarde toestaan. Null-patronen worden vaak gebruikt bij het samenwerken met .NET Framework-code. De retourwaarde van een .NET-API kan bijvoorbeeld de invoer zijn voor een match-expressie. U kunt de programmastroom beheren op basis van of de retourwaarde null is en ook op andere kenmerken van de geretourneerde waarde. U kunt het null-patroon gebruiken om te voorkomen dat null-waarden worden doorgegeven aan de rest van uw programma.

In het volgende voorbeeld worden het null-patroon en het variabelepatroon gebruikt.

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

Null-patroon wordt ook aanbevolen voor de nullbaarheid functionaliteiten van F# 9 :

let len (str: string | null) =
    match str with
    | null -> -1
    | s -> s.Length

Op dezelfde manier kunt u nieuwe nullability-gerelateerde patronen gebruiken:

let let str =       // str is inferred to be `string | null`
    match str with
    | Null -> -1
    | NonNull (s: string) -> s.Length

Naam van patroon

Het nameof patroon komt overeen met een tekenreeks wanneer de waarde gelijk is aan de expressie die volgt op het nameof trefwoord. bijvoorbeeld:

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

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

Zie de operator nameof voor informatie over wat u een naam kunt geven.

Zie ook