Rozpoznávání vzorů
Vzory jsou pravidla pro transformaci vstupních dat. Používají se v rámci jazyka F# k porovnání dat s logickou strukturou nebo strukturami, rozdělení dat do základních částí nebo k extrakci informací z dat různými způsoby.
Poznámky
Vzory se používají v mnoha konstruktorech jazyka, jako je například výraz match
. Používají se při zpracování argumentů pro funkce v let
vazbách, výrazech lambda a v obslužných rutinách výjimek přidružených k výrazu try...with
. Další informace naleznete v tématu match expressions, let bindings, lambda Expressions: The fun
Keyworda exceptions: The try...with
Expression.
Například ve výrazu match
je vzor tím, co následuje za symbolem svislé roury.
match expression with
| pattern [ when condition ] -> result-expression
...
Každý vzor funguje jako pravidlo pro transformaci vstupu nějakým způsobem. Ve výrazu match
se každý vzor prochází, aby se zjistilo, zda jsou vstupní data ve shodě se vzorem. Pokud se najde shoda, provede se výsledný výraz. Pokud se shoda nenajde, otestuje se další pravidlo vzoru. Volitelná podmínka část je vysvětlena v části Výrazy pro shodu.
Podporované vzory jsou uvedené v následující tabulce. V době běhu se vstup testuje pro každý z následujících vzorů v pořadí uvedeném v tabulce a vzory se použijí rekurzivně, od prvního po poslední, jakmile se zobrazí v kódu, a zleva doprava pro vzory na každém řádku.
Jméno | Popis | Příklad |
---|---|---|
Konstantní vzor | Libovolný číselný, znakový nebo řetězcový literál, konstanta výčtu nebo definovaný identifikátor literálu |
1.0 , "test" , 30 , Color.Red |
Vzor identifikátoru | Hodnota případu diskriminované unie, značka výjimky nebo případ aktivního vzoru | Some(x) Failure(msg) |
Vzor proměnné | identifikátor | a |
vzor as |
jako vzor jako identifikátor | (a, b) as tuple1 |
Vzorec OR | vzor1 | vzor2 | ([h] | [h; _]) |
Vzor AND | vzor1 & vzor2 | (a, b) & (_, "test") |
Vzor nevýhod | identifikátor :: identifikátor seznamu | h :: t |
Vzor seznamu | [ pattern_1; ... ; pattern_n ] | [ a; b; c ] |
Vzor pole | [| pattern_1; ..; pattern_n |] | [| a; b; c |] |
Vzor v závorkách | ( vzor ) | ( a ) |
Vzor n-tice | ( vzor_1, ... , vzor_n ) | ( a, b ) |
Vzor záznamu | { identifier1 = pattern_1; ... ; identifier_n = pattern_n } | { Name = name; } |
Vzor se zástupnými znaky | _ | _ |
Vzor společně s poznámkami k typu | vzor : typ | a : int |
Model testu typu | :? typ [ jako identifikátor ] | :? System.DateTime as dt |
Nulový vzor | nula | null |
Název vzoru | název výrazu | nameof str |
Konstantní vzory
Konstantní vzory jsou číselné, znakové a řetězcové literály, výčtové konstanty (včetně názvu typu výčtu). Výraz match
, který má pouze konstantní vzory, lze porovnat s příkazem case v jiných jazycích. Vstup je porovnán s doslovnou hodnotou a vzor se shoduje, pokud jsou hodnoty stejné. Typ literálu musí být kompatibilní s typem vstupu.
Následující příklad ukazuje použití literálových vzorů a také používá proměnný vzor 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 literálového vzoru je model založený na konstantách výčtu. Při použití konstant výčtu je nutné 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
Vzory identifikátorů
Pokud je vzor řetězcem znaků, které tvoří platný identifikátor, určuje formulář identifikátoru, jak se vzor shoduje. Pokud je identifikátor delší než jeden znak a začíná velkým znakem, kompilátor se pokusí vytvořit shodu se vzorem identifikátoru. Identifikátor tohoto vzoru může být hodnota označená atributem Literal, diskriminovaný unijní případ, identifikátor výjimky nebo aktivní vzorový případ. Pokud se nenajde žádný odpovídající identifikátor, shoda selže a další pravidlo vzoru, proměnlivý vzor, se porovná se vstupem.
Diskriminované vzory sjednocení mohou být jednoduché pojmenované případy nebo mohou mít hodnotu nebo řazenou kolekci členů obsahující více hodnot. Pokud je hodnota, musíte zadat identifikátor hodnoty. V případě n-tice musíte dodat vzor n-tice s identifikátorem pro každý prvek n-tice nebo identifikátor s názvem pole pro jedno či více pojmenovaných polí sjednocení. Příklady najdete v příkladech kódu v této části.
Typ option
je diskriminovaná unie, která má dva případy, Some
a None
. Jeden případ (Some
) má hodnotu, ale druhý (None
) je jen pojmenovaný případ. Proto Some
musí mít proměnnou pro hodnotu přidruženou k Some
případu, ale None
musí být zobrazena samostatně. V následujícím kódu je proměnné var1
přiřazena hodnota získaná při odpovídající 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 kombinaci řetězců a znaků, které představují možné formy jmen. Případy diskriminované unie 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 diskriminovaná sjednocení, která mají pojmenovaná pole, použijete znak rovnosti (=) pro extrakci hodnoty z pojmenovaného pole. Představte si 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 vzorovém shodném výrazu 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é, takže v předchozím příkladu mají oba Circle(r)
i Circle(radius = r)
stejný účinek.
Při zadávání více polí použijte středník (;) jako oddělovač.
match shape with
| Rectangle(height = h; width = w) -> printfn $"Rectangle with height %f{h} and width %f{w}"
| _ -> ()
Aktivní vzory vám umožňují definovat složitější vlastní porovnávání vzorů. Další informace o aktivních vzorech naleznete v tématu aktivní vzory.
Případ, ve kterém je identifikátor výjimkou, se používá ve vzorovém porovnávání v kontextu obslužných rutin výjimek. Informace o porovnávání vzorů při zpracování výjimek naleznete v tématu Výjimky: Výraz try...with
.
Vzory proměnných
Vzor proměnné přiřadí hodnotu, která se shoduje s názvem proměnné, která je pak k dispozici pro použití ve výrazu spuštění napravo od symbolu ->
. Samotný vzor proměnných odpovídá jakémukoli vstupu, ale vzory proměnných se často objevují v jiných vzorech, a proto umožňují složitější struktury, jako jsou řazené kolekce členů a pole, rozdělit do proměnných.
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 vzor, který má připojenou klauzuli as
. Klauzule as
sváže shodnou hodnotu s názvem, který lze použít ve výrazu provádění výrazu match
nebo v případě, že se tento vzor používá v vazbě let
, název se přidá jako vazba k místnímu oboru.
Následující příklad používá vzor as
.
let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1
OR vzor
Vzor OR se používá, když vstupní data můžou odpovídat více vzorům a chcete spustit stejný kód jako výsledek. Typy obou stran vzoru OR musí být kompatibilní.
Následující příklad ukazuje vzor 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
Model AND vyžaduje, aby vstup odpovídal dvěma vzorům. Typy obou stran vzoru AND musí být kompatibilní.
Následující příklad je podobný jako detectZeroTuple
, který je zobrazen v části Vzor n-tice později v tomto tématu, ale zde jsou var1
i var2
získány jako hodnoty pomocí AND vzoru.
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)
Vzor nevýhody
Vzorec cons se používá k rozkladu seznamu na první prvek, hlavu, a seznam, který obsahuje zbývající prvky, ocas.
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 rozdělení seznamů na několik prvků. Samotný vzor seznamu se může shodovat pouze se seznamy 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
Maticový vzor se podobá vzoru seznamu a lze ho použít k dekompilování polí s konkrétní délkou.
// 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 [| |] )
Vzor závorek
Závorky lze seskupit podle vzorů, aby bylo dosaženo požadované asociativity. V následujícím příkladu se závorky používají k řízení asociativity mezi vzorem AND a vzorem záporů.
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 n-tice
Vzor n-tice odpovídá vstupu ve formě n-tice a umožňuje rozdělení n-tice na její složky pomocí proměnných vzoru pro každou pozici v rámci n-tice.
Následující příklad ukazuje vzor n-tice a také používá literální vzory, vzory proměnných a zástupný vzor.
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 dekompose záznamů pro extrahování hodnot polí. Vzor nemusí odkazovat na všechna pole záznamu; všechna vynechaná pole se neúčastní shody 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"
Vzor zástupných znaků
Vzor zástupného znaku je reprezentován znakem podtržítka (_
) a odpovídá jakémukoli vstupu, stejně jako vzor proměnné, s tím rozdílem, že vstup se místo přiřazení proměnné zahodí. Vzor se zástupnými znaky se často používá v jiných vzorech jako zástupce pro hodnoty, které nejsou ve výrazu napravo od symbolu ->
potřeba. Vzor se zástupnými znamény se také často používá na konci seznamu vzorů, aby odpovídal jakémukoli chybějícímu vstupu. Vzor zástupných znaků je ukázaný v mnoha příkladech kódu v tomto tématu. Příklad najdete v předchozím kódu.
Vzory s typovými anotacemi
Vzory můžou obsahovat poznámky k typu. Chovají se jako jiné poznámky k typům a vedou odvození jako jiné poznámky k typům. U poznámek typu ve vzorech se vyžadují závorky. Následující kód ukazuje vzor, který má typovou anotaci.
let detect1 x =
match x with
| 1 -> printfn "Found a 1!"
| (var1 : int) -> printfn "%d" var1
detect1 0
detect1 1
Model testu typu
Vzor testu typu se používá ke spárování vstupu s typem. Pokud je vstupní typ shodný s typem (nebo odvozeným typem) typu zadaného v vzoru, shoda bude úspěšná.
Následující příklad ukazuje 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."
| _ -> ()
Pokud kontrolujete, zda je identifikátor konkrétního odvozeného typu, nepotřebujete část vzoru as identifier
, jak je ukázáno v následujícím příkladu:
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"
| _ -> ()
Nullový vzor
Vzor null odpovídá hodnotě null, která se může zobrazit při práci s typy, které umožňují hodnotu null. Vzory null se často používají při spolupráci s kódem rozhraní .NET Framework. Návratovou hodnotou rozhraní .NET API může být například vstup do výrazu match
. Tok programu můžete řídit na základě toho, jestli je vrácená hodnota null, a také na dalších vlastnostech vrácené hodnoty. Pomocí vzoru null můžete zabránit šíření hodnot null do zbytku 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()
Doporučuje se také vzor null pro funkce nulovatelnosti ve F# 9 .
let len (str: string | null) =
match str with
| null -> -1
| s -> s.Length
Podobně můžete použít nové vzory související s vyhrazenou hodnotou null :
let let str = // str is inferred to be `string | null`
match str with
| Null -> -1
| NonNull (s: string) -> s.Length
Název vzoru
Vzor nameof
odpovídá řetězci, pokud se jeho hodnota rovná výrazu, který následuje za klíčovým slovem nameof
. například:
let f (str: string) =
match str with
| nameof str -> "It's 'str'!"
| _ -> "It is not 'str'!"
f "str" // matches
f "asdf" // does not match
Informace o tom, co lze pojmenovat, najdete u operátoru nameof
.