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 aan de try...with expressie zijn gekoppeld. Zie Vergelijkingsexpressies, bindingen, lambda-expressies: het fun trefwoord en uitzonderingen: De try...with expressie voor meer informatie.

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. Het optionele onderdeel wanneer voorwaarde wordt uitgelegd in Overeenkomstexpressies.

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.

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

Failure(msg)
Variabel patroon id a
as Patroon patroon als id (a, b) as tuple1
OR-patroon pattern1 | pattern2 ([h] | [h; _])
AND-patroon pattern1 & pattern2 (a, b) & (_, "test")
Nadelenpatroon id :: list-identifier h :: t
Lijstpatroon [ pattern_1; ... ; pattern_n ] [ a; b; c ]
Matrixpatroon [| pattern_1; ..; pattern_n |] [| a; b; c |]
Haakje-patroon ( patroon ) ( a )
Tuple-patroon ( pattern_1, ... , pattern_n ) ( a, b )
Recordpatroon { id1 = pattern_1; ... ; = identifier_n pattern_n } { Name = name; }
Jokertekenpatroon _ _
Patroon samen met typeaantekening patroon : type a : int
Typetestpatroon :? type [ als id ] :? System.DateTime as dt
Null-patroon Nul null
Naam van patroon naamvan 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 id vormt, bepaalt de vorm van de id hoe het patroon overeenkomt. 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 id voor dit patroon kan een waarde zijn die is gemarkeerd met het kenmerk Letterlijk, een gediscrimineerde samenvoegingscase, een uitzonderings-id 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. Some Daarom moet er een variabele zijn voor de waarde die aan de Some case is gekoppeld, maar None moet zelf worden weergegeven. In de volgende code krijgt de variabele var1 de waarde die wordt verkregen door overeen te komen met de Some case.

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 vereniging 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 samenvoegingen die benoemde velden hebben, gebruikt u het gelijkteken (=) om de waarde van een benoemd veld op te halen. 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 beide Circle(r) hetzelfde Circle(radius = r) 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 patronen voor meer informatie over actieve patronen.

Het geval waarin de id een uitzondering is, wordt gebruikt in patroonkoppeling in de context van uitzonderingshandlers. Zie Uitzonderingen: De try...with expressie voor informatie over patroonkoppeling in de afhandeling van uitzonderingen.

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 waaraan een as component is 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 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 beide zijden van het AND-patroon moeten compatibel zijn.

Het volgende voorbeeld is zoals detectZeroTuple weergegeven in de sectie Tuple-patroon verderop in dit onderwerp, maar hier worden beide var1 verkregen var2 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 op te delen in het eerste element, het hoofd en een lijst met de resterende elementen, de staart.

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 worden gegroepeerd rond patronen 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 tuple-patroon komt overeen met invoer in het tuple-formulier en stelt de tuple in staat om te worden uitgesplitsd in de samenstellende elementen met behulp van patroonkoppelingsvariabelen 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)

Recordpatroon

Het recordpatroon wordt gebruikt om records op te extraheren om 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 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 jokertekenpatroon wordt ook vaak gebruikt aan het einde van een lijst met patronen om eventuele niet-overeenkomende invoer te vinden. 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 leiden deductie 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

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

Null-patroon

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 voor een match expressie zijn. 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()

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. Voorbeeld:

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 nameof operator voor informatie over wat u kunt noemen.

Zie ook