Sdílet prostřednictvím


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

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.

Viz také