Sdílet prostřednictvím


Porovná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 match je výraz. 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 najdete v tématu Shoda výrazů, let vazby, výrazy lambda: fun Klíčové slovo a výjimky: Výraztry...with.

Například ve výrazu matchje vzorem to, 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 jednotlivé vzory prověří a zjistí, jestli jsou vstupní data kompatibilní se vzorem. Pokud se najde shoda, provede se výsledný výraz. Pokud se shoda nenajde, otestuje se další pravidlo vzoru. Volitelná část podmínky je vysvětlena ve výrazech shody.

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.

Název 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é sjednocení, popisku výjimky nebo aktivního vzorového případu Some(x)

Failure(msg)
Vzor proměnných identifikátor a
as Vzor pattern as identifier (a, b) as tuple1
Model OR pattern1 | pattern2 ([h] | [h; _])
Vzor AND pattern1 & pattern2 (a, b) & (_, "test")
Vzor záporů identifikátor :: list-identifier h :: t
Vzor seznamu [ pattern_1; ... ; pattern_n ] [ a; b; c ]
Vzor pole [| pattern_1; ..; pattern_n |] [| a; b; c |]
Vzor závorek ( vzor ) ( a )
Vzor řazené kolekce členů ( pattern_1, ... , pattern_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 pattern : type a : int
Model testu typu :? type [ as identifier ] :? System.DateTime as dt
Model Null null null
Název vzoru nameof expr 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 se porovná s hodnotou literálu a vzor odpovídá, 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ým sjednocovacího případu, identifikátorem výjimky nebo aktivním vzorovým případem. Pokud se nenajde žádný odpovídající identifikátor, shoda selže a další pravidlo vzoru 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ě řazené kolekce členů musíte zadat vzor řazené kolekce členů s identifikátorem pro každý prvek řazené kolekce členů nebo identifikátor s názvem pole pro jedno nebo více pojmenovaných sjednocovacích polí. Příklady najdete v příkladech kódu v této části.

Typ option je diskriminovaná sjednocení, která má dva případy, Some a None. Jeden případ (Some) má hodnotu, ale druhý (None) je jen pojmenovaný případ. Some Proto musí mít proměnnou pro hodnotu přidruženou Some k případu, ale None musí se objevit samostatně. V následujícím kódu má proměnná var1 hodnotu získanou odpovídající Some případu.

let printOption (data : int option) =
    match data with
    | Some var1  -> printfn "%d" var1
    | None -> ()

V následujícím příkladu PersonName diskriminovaná sjednocení obsahuje kombinaci řetězců a znaků, které představují možné formy názvů. 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 znaménko rovná se (=) k extrahování hodnoty pojmenovaného pole. Představte si například diskriminovanou 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) a 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 umožňují definovat složitější porovnávání vlastních vzorů. Další informace o aktivníchvzorch

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ýraztry...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 řazené kolekce členů.

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á k němu připojenou as klauzuli. Klauzule as sváže shodnou hodnotu s názvem, který lze použít ve výrazu match provádění výrazu, nebo v případě, kdy se tento vzor používá v let vazbě, název se přidá jako vazba k místnímu oboru.

Následující příklad používá as vzor.

let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1

OR Pattern

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 v části Vzor řazené kolekce členů dále v tomto tématu, ale zde oba var1 a var2 jsou 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)

Vzor nevýhody

Vzor záporů slouží k rozkladu seznamu do prvního prvku, hlavy a seznamu, 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 řazené kolekce členů

Vzor řazené kolekce členů odpovídá vstupu ve formuláři řazené kolekce členů a umožňuje rozdělení řazené kolekce členů do jejích základních prvků pomocí vzorových odpovídajících proměnných pro každou pozici v řazené kolekci členů.

Následující příklad ukazuje vzor řazené kolekce členů a také používá literální vzory, vzory proměnných a zástupný 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 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ých znaků 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ástupný symbol pro hodnoty, které nejsou ve výrazu potřeba napravo od symbolu -> . 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 poznámkami typu

Vzory můžou obsahovat poznámky k typu. Chovají se stejně jako jiné poznámky k typům a odvozují vodítka jako jiné poznámky typu. U poznámek typu ve vzorech se vyžadují závorky. Následující kód ukazuje vzor, který má poznámku k typu.

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, jestli je identifikátor konkrétního odvozeného typu, nepotřebujete as identifier část vzoru, jak je znázorně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"
    | _ -> ()

Model 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 se často používají při spolupráci s kódem rozhraní .NET Framework. Například návratová hodnota rozhraní .NET API může být vstupem 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()

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 můžete pojmenovat, najdete v nameof operátoru.

Viz také