Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
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", , 30Color.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_npattern_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 | null | 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, LastOnlya 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
Můžete také zřetězit několik vzorů záporů, aby odpovídaly seznamům, které začínají konkrétními sekvencemi prvků.
let charList = ['A'; 'B'; 'C'; 'D']
// This example demonstrates multiple cons patterns.
let matchChars xs =
match xs with
| 'A'::'B'::t -> printfn "starts with 'AB', rest: %A" t
| 'A'::t -> printfn "starts with 'A', rest: %A" t
| 'C'::'D'::t -> printfn "starts with 'CD', rest: %A" t
| _ -> printfn "does not match"
matchChars charList
matchChars ['A'; 'X']
matchChars ['C'; 'D'; 'E']
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.
Následující kód ukazuje několik dalších použití zástupného znaku:
// Wildcard pattern matching "nothing" examples
// Example 1: Wildcard ignoring function parameters
let ignoreAllParams _ _ = "Ignores all input"
// Example 2: Wildcard in destructuring, ignoring elements
let getFirstOnly (first, _) = first
// Example 3: Using wildcard to ignore optional values
let handleEmpty opt =
match opt with
| Some _ -> "Has something"
| None -> "Has nothing"
// Usage
printfn "%s" (ignoreAllParams 42 "test")
printfn "%d" (getFirstOnly (1, "ignored"))
printfn "%s" (handleEmpty None)
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.
Vzor s poznámkou typu používá syntaxi pattern : type a poskytuje informace o typu kompilace pro kontrolu typů. Toto je čistě statická poznámka typu, která pomáhá s odvozováním typu – neprovádí žádnou kontrolu typů za běhu ani převod. Kompilátor tyto informace používá během kompilace k určení typu proměnné vzoru.
Následující kód ukazuje vzor, který má poznámku typu:
let detect1 x =
match x with
| 1 -> printfn "Found a 1!"
| (var1 : int) -> printfn "%d" var1
detect1 0
detect1 1
V tomto příkladu říká kompilátoru, (var1 : int) že var1 je typu int. Tento problém se vyřeší v době kompilace a vygenerovaný kód považuje var1 celé číslo ve výrazu shody. Tento vzor bude odpovídat libovolné celočíselné hodnotě a sváže ji s var1.
Klíčové charakteristiky:
- Používá syntaxi
pattern : type(s jednou dvojtečku). - Vyřešeno v době kompilace – poskytuje kontrole typů informace o typu.
- Neprovádí testování typů modulu runtime.
- Používá se k odvozování typů a k vedení kompilátoru.
Model testu typu
Vzor testu typu se používá ke spárování vstupu s typem za běhu. Pokud je vstupní typ shodný s typem (nebo odvozeným typem) typu zadaného v vzoru, shoda bude úspěšná.
Vzor testu typu používá syntaxi :? type a provádí kontrolu typů modulu runtime, podobně jako is operátory v as jazyce C#. Tento vzor testuje, jestli je hodnota určitého typu během provádění programu, což je užitečné při práci s hierarchiemi dědičnosti nebo implementacemi rozhraní.
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"
| _ -> ()
Klíčové charakteristiky:
- Používá syntaxi
:? typenebo:? type as identifier(se otazníkem). - Vyřešeno za běhu – provádí skutečnou kontrolu typů během provádění.
- Testuje, jestli je hodnota instance určitého typu nebo jeho odvozených typů.
- Běžně se používá s hierarchiemi dědičnosti a polymorfními typy.
- Podobá se operátoru nebo
asoperátoru jazykaisC#.
Kontrastní poznámky k typům a vzory testů typů
I když oba vzory zahrnují typy, slouží velmi různým účelům:
| Vlastnost | Model anotace typu (pattern : type) |
Model testování typů (:? type) |
|---|---|---|
| Syntax | Jedno dvojtečka: a : int |
Dvojtečka s otazníkem: :? Button |
| Při řešení | Čas kompilace | Runtime |
| Purpose | Odvození typu vodítka | Testuje skutečný typ hodnoty. |
| Případ použití | Pomoc kompilátoru pochopit typy | Kontrola typů modulu runtime v hierarchiích dědičnosti |
| Ekvivalent v jazyce C# | Psaní poznámek v vzorech přepínačů |
is operátory nebo as operátory |
Následující příklad ukazuje rozdíly:
// Type annotation pattern - compile time
let detect1 x =
match x with
| 1 -> printfn "Found a 1!"
| (var1 : int) -> printfn "%d" var1
// The ': int' tells the compiler var1 is an int
// Everything is resolved at compile time
// Type test pattern - runtime
type A() = class end
type B() = inherit A()
let test (a: A) =
match a with
| :? B -> printfn "Runtime check: it's a B"
| _ -> printfn "Runtime check: it's not a B"
// The ':? B' performs a runtime type check
// The actual type is tested during execution
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 len 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. Tento vzor je užitečný zejména v případě, že potřebujete shodovat řetězcové hodnoty s názvy typů, diskriminovanými případy sjednocení nebo jinými symboly v kódu. Použití nameof poskytuje bezpečnost v době kompilace, protože pokud přejmenujete symbol, vzor automaticky použije nový název.
Běžným případem použití je deserializace dat, kde řetězcové hodnoty představují názvy typů nebo malých písmen:
type EventType =
| OrderCreated
| OrderShipped
| OrderDelivered
let handleEvent eventName data =
match eventName with
| nameof OrderCreated -> printfn "Processing order creation: %s" data
| nameof OrderShipped -> printfn "Processing order shipment: %s" data
| nameof OrderDelivered -> printfn "Processing order delivery: %s" data
| _ -> printfn "Unknown event type: %s" eventName
handleEvent "OrderCreated" "Order #123" // matches first case
handleEvent "OrderShipped" "Order #123" // matches second case
Tento přístup je lepší než použití řetězcových literálů (například "OrderCreated") z následujících důvodů:
- Pokud přejmenujete
OrderCreatednaOrderPlaced, vzor se automaticky aktualizuje. - Kompilátor zajišťuje, že symbol existuje a brání překlepům.
- Váš kód zůstává konzistentní při refaktoringu.
Můžete také použít nameof s parametry:
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.