Porovnávání vzorů (F#)
Vzorky jsou pravidla pro transformování vstupních dat.Používají se v celém jazyce F# k porovnání dat s logickou strukturou nebo strukturami, rozložení dat na základní prvky nebo extrahování informací z dat různými způsoby.
Poznámky
Vzorky se používají v mnoha konstrukcích jazyka, jako je výraz match.Používají se při zpracování argumentů pro funkce ve vazbách let, výrazech lambda a v obslužných rutinách výjimek spojených s výrazem try...with.Další informace naleznete v tématu Výrazy shody (F#), let – vazby (F#), Výrazy lambda: Klíčové slovo fun (F#) a Výjimky: Výraz try...with (F#).
Například ve výrazu match je pattern tím, co následuje symbol svislé čáry.
spojit expression s
| pattern [když condition ] –> result-expression
...
Každý vzorek se chová jako pravidlo pro transformování vstupu nějakým způsobem.U výrazu match je zkoumán každý vzorek, zda jsou vstupní data kompatibilní se vzorkem.Pokud je nalezena shoda, je výsledkem spuštění výrazu.Pokud není nalezena shoda, je testováno další pravidlo vzorku.Volitelně při vysvětlení části condition v Výrazy shody (F#).
Podporované vzory jsou uvedeny v následující tabulce.Při spuštění je vstup je testován oproti každému z následujících vzorů v pořadí uvedeném v tabulce, a vzory se používají rekurzivně, od prvního až poslední, tak jak se objevují ve vašem kódu a zleva doprava pro vzory na každém řádku.
Název |
Description |
Příklad |
---|---|---|
Konstantní vzorek |
Všechny číselné, znakové nebo řetězcové literály, konstanta výčtu nebo definovaný identifikátor literálu |
1.0, "test", 30, Color.Red |
Vzorek identifikátoru |
Hodnota case rozlišovaného sjednocení, popisku výjimky nebo aktivní hodnoty case vzoru |
Some(x) Failure(msg) |
Vzorec proměnné |
identifier |
a |
Vzor as |
pattern jako identifier |
(a, b) as tuple1 |
NEBO vzor |
pattern1 | pattern2 |
([h] | [h; _]) |
Vzor AND |
pattern1 & pattern2 |
(a, b) & (_, "test") |
Nevýhody vzoru |
identifier :: list-identifier |
h :: t |
Vzor seznamu |
[ pattern_1; ... ; pattern_n ] |
[ a; b; c ] |
Vzor pole |
[| pattern_1; ..; pattern_n ] |
[| a; b; c |] |
Vzor v závorce |
( pattern ) |
( a ) |
Vzor řazené kolekce členů |
( pattern_1, ... , pattern_n ) |
( a, b ) |
Vzor záznamu |
{ identifier1 = pattern_1; ... ; identifier_n = pattern_n } |
{ Name = name; } |
Vzorec zástupných znaků |
_ |
_ |
Vzorek společně s anotací typu |
pattern : type |
a : int |
Testovací vzorek typu |
:?type [ jako identifier ] |
:? System.DateTime as dt |
Vzor Null |
null |
null |
Konstantní vzorky
Konstantní vzorky jsou čísla, znaky a řetězcové literály výčtu konstant (se zahrnutým názvem typu výčtu).Výraz match, který má pouze konstantní vzory, je možné porovnat s příkazy case v jiných jazycích.Vstup je porovnán s hodnotou literálu a pokud jsou hodnoty stejné, odpovídá vzoru.Typ literálu musí být kompatibilní s typem vstupu.
Následující příklad ukazuje použití vzorů literálu a také používá vzory proměnné a vzor OR.
[<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
Dalším příkladem vzoru literálu je vzor založený na výčtu konstant.Při použití konstant výčtu musíte zadat název typu výčtu.
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
Vzorky identifikátoru
Pokud vzorek je řetězec znaků, který tvoří platný identifikátor, forma identifikátoru určuje, jak je vzorek párován.Pokud identifikátor je delší než jeden znak a začíná velkým písmenem, kompilátor se pokusí spárovat se vzorkem identifikátoru.Identifikátor pro tento vzorek může být hodnota označená jako literální atribut, případ diskriminovaného sjednocení, identifikátor výjimky nebo případ aktivního vzoru.Pokud není nalezen žádný odpovídající identifikátor, shoda se nezdaří a další pravidlo pro vzorek, vzorek proměnné, se porovná se vstupem.
Vzorky rozlišeného sjednocení mohou být jednoduše pojmenované případy nebo mohou mít hodnotu či n-tici obsahující více hodnot.Pokud existuje hodnota, musíte zadat identifikátor pro hodnotu.V případě n-tice musíte zadat vzor n-tice s identifikátorem pro každý element n-tice nebo identifikátor s názvem pole pro jedno nebo více sjednocených polí.Příklady kódu naleznete v této části s příklady.
Typ option představuje diskriminované sjednocení, které má dva případy - Some a None.Jeden případ (Some) má hodnotu, ale další (None) je pouze pojmenovaný případ.Proto musí mít Some proměnnou pro hodnotu spojenou s případem Some, ale None se musí zobrazovat samostatně.V následujícím kódu je proměnné var1 přiřazena hodnota získaná pomocí odpovídajícího případu Some.
let printOption (data : int option) =
match data with
| Some var1 -> printfn "%d" var1
| None -> ()
V následujícím příkladu diskriminovaná unie PersonName obsahuje směs řetězců a znaků, které představují možné formy jména.Případy diskriminovaného sjednocení jsou FirstOnly, LastOnly a 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
Pro rozlišovaná sjednocení, která mají pojmenované názvy polí, použijte znaménko rovnítko (=) k získání hodnoty pojmenovaného pole.Zvažte například diskriminované sjednocení s deklarací, jako je následující.
type Shape =
| Rectangle of height : float * width : float
| Circle of radius : float
Pojmenovaná pole můžete použít ve výrazu odpovídajícímu vzorci následujícím způsobem.
let matchShape shape =
match shape with
| Rectangle(height = h) -> printfn "Rectangle with length %f" h
| Circle(r) -> printfn "Circle with radius %f" r
Použití pojmenovaného pole je volitelné, protože v předchozím příkladu má Circle(r) a Circle(radius = r) stejný efekt.
Pokud zadáte více polí, použijte středník (;) jako oddělovač.
match shape with
| Rectangle(height = h; width = w) -> printfn "Rectangle with height %f and width %f" h w
| _ -> ()
Aktivní vzorky umožňují definovat složitější vlastní porovnávání vzorků.Další informace o aktivních vzorech naleznete v části Aktivní vzorky (F#).
Případ, ve kterém je identifikátor výjimkou, se používá pro porovnávání se vzory v kontextu obslužných rutin výjimek.Informace o vzoru porovnávání ve zpracování výjimek naleznete v tématu Výjimky: Výraz try...with (F#).
Vzorce proměnné
Variabilní vzor přiřadí hodnotu porovnávanou s názvem proměnné, která je pak k dispozici pro použití v prováděném výrazu vpravo od symbolu ->.Variabilní vzor odpovídá jakémukoliv vstupu, ale variabilní vzorky se často objevuje v rámci jiných vzorků, proto umožňuje složitější struktury, jako například záznamy a pole, které lze rozložit na proměnné.
Následující příklad ukazuje vzor proměnné v rámci vzoru n-tice.
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)
jako Vzor
Vzor as je vzorkem, ke kterému je připojena klauzule as.Klauzule as váže odpovídající hodnotu k názvu, který lze použít při definici spuštění výrazu match, nebo v případě použití tohoto vzoru ve vazbě let pro přidání názvu jako vazby v místním rozsahu.
V následujícím příkladu je použit vzor as.
let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1
NEBO vzor
Vzor OR se používá, když vstupní data mohou odpovídat více vzorům a chcete provést ve výsledku stejný kód.Typy obou stran vzoru operátoru OR musí být kompatibilní.
Následující příklad demonstruje vzory OR.
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)
Vzor AND
Vzor AND vyžaduje, aby byla na vstupu shoda dvou vzorků.Typy obou stran vzoru operátoru AND musí být kompatibilní.
Následující příklad je podobný detectZeroTuple uvedeného v části Vzor n-tice dále v tomto tématu, ale zde jsou var1 a var2 získány jako hodnoty pomocí vzoru AND.
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)
Nevýhody vzoru
Vzorek nevýhod se používá rozložení seznamu na první prvek, záhlaví a seznam, který obsahuje zbývající prvky – zakončení.
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
Vzor seznamu
Vzor seznamu umožňuje rozložit seznamy na prvky.Vzor seznamu sám může odpovídat pouze seznamům určitého počtu prvků.
// 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 [ ] )
Vzor pole
Vzorec pole se podobá vzoru seznamu a lze jej použít k rozložení polí určité délky.
// 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 [| |] )
Vzor v závorce
Závorky mohou být seskupeny kolem vzorků k dosažení požadované asociativity.V následujícím příkladu jsou použity závorky pro řízení asociativity mezi vzorcem AND a vzorcem nevýhod.
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
Vzor řazené kolekce členů
Vzorec řazené kolekce členů odpovídá formě řazené kolekce členů a umožňuje její rozložení na základní prvky pomocí proměnných porovnání vzorce pro každou pozici v řazené kolekci členů.
Následující příklad ukazuje vzor n-tice a také používá vzor literálu, vzory proměnné a vzor zástupných znaků.
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)
Vzor záznamu
Vzor záznamu slouží k rozložení záznamů a následné extrahování hodnot polí.Vzor neobsahuje odkaz na všechna pole záznamu. Vynechaná pole pouze nejsou součástí srovnávání a nejsou extrahována.
// 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"
Vzorec zástupných znaků
Vzorec zástupných znaků je vyjádřen podtržítkem (_) a odpovídá jakémukoli zadání, stejně jako vzorec proměnné s tou výjimkou, že je vstup ignorován místo toho, aby byl přiřazen proměnné.Maska zástupných znaků se často používá v rámci jiných vzorků jako zástupný symbol pro hodnoty, které nejsou potřeba ve výrazu vpravo od symbolu ->.Vzorec zástupných znaků se také často používá na konci seznamu vzorků tak, aby odpovídal jakémukoli neodpovídajícímu vstupu.Maska zástupných znaků je znázorněna v mnoha příkladech kódu v tomto tématu.Příklad viz předchozí kód.
Vzorky, které mají anotace typu
Vzorky mohou mít anotace typu.Chovají se jako ostatní poznámky typu a příručka pro odvození stejně jako ostatní poznámky typu.Jsou vyžadovány závorky kolem anotace typu ve vzorcích.Následující kód popisuje vzor, který má anotaci typu.
let detect1 x =
match x with
| 1 -> printfn "Found a 1!"
| (var1 : int) -> printfn "%d" var1
detect1 0
detect1 1
Testovací vzorek typu
Vzor testovacího typu slouží k porovnání vstupu oproti typu.Pokud je vstupní typ porovnán s (nebo odvozeným typem) typem určeným ve vzorku, porovnávání je úspěšné.
Následující příklad demonstruje vzor testu typu.
open System.Windows.Forms
let RegisterControl(control:Control) =
match control with
| :? Button as button -> button.Text <- "Registered."
| :? CheckBox as checkbox -> checkbox.Text <- "Registered."
| _ -> ()
Vzor Null
Vzor Null odpovídá hodnotě null, která se ,může zobrazit při práci s typy, které umožňují hodnotu null.Vzory Null jsou často používány při spolupráci s kódem .NET Framework.Například hodnota vrácená pomocí .NET API může být vstup do výrazu match.Můžete řídit tok programu na základě toho, zda je vrácená hodnota null a také na jiných vlastnostech vrácené hodnoty.Vzorec hodnot null můžete použít k zabránění šíření hodnot null do ostatních částí programu.
Následující příklad používá vzor null a vzor proměnné.
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()