Dopasowanie wzorca
Wzorce to reguły przekształcania danych wejściowych. Są one używane w języku F# do porównywania danych ze strukturą logiczną lub strukturą, rozkładania danych na części składowe lub wyodrębniania informacji z danych na różne sposoby.
Uwagi
Wzorce są używane w wielu konstrukcjach językowych, takich jak match
wyrażenie. Są one używane podczas przetwarzania argumentów funkcji w let
powiązaniach, wyrażeniach lambda i w programach obsługi wyjątków skojarzonych z wyrażeniem try...with
. Aby uzyskać więcej informacji, zobacz Dopasowywanie wyrażeń, let Bindings, Lambda Expressions: The fun
Keyword i Exceptions: The try...with
Expression ( Wyrażenia wyrażenia lambda: słowo kluczowe i wyjątki).
Na przykład w wyrażeniu match
wzorzec jest zgodny z symbolem potoku.
match expression with
| pattern [ when condition ] -> result-expression
...
Każdy wzorzec działa jako reguła przekształcania danych wejściowych w jakiś sposób. W wyrażeniu match
każdy wzorzec jest sprawdzany z kolei, aby sprawdzić, czy dane wejściowe są zgodne ze wzorcem. Jeśli zostanie znalezione dopasowanie, zostanie wykonane wyrażenie wyniku. Jeśli dopasowanie nie zostanie znalezione, zostanie przetestowana kolejna reguła wzorca. Opcjonalnie, gdy część warunku jest objaśniona w wyrażeniach dopasowania.
Obsługiwane wzorce przedstawiono w poniższej tabeli. W czasie wykonywania dane wejściowe są testowane względem każdego z poniższych wzorców w kolejności wymienionej w tabeli, a wzorce są stosowane rekursywnie, od pierwszego do ostatniego, gdy pojawiają się w kodzie, a od lewej do prawej dla wzorców w każdym wierszu.
Nazwa/nazwisko | opis | Przykład |
---|---|---|
Wzorzec stałej | Dowolny literał liczbowy, znak lub ciąg, stała wyliczenia lub zdefiniowany identyfikator literału | 1.0 , , "test" , , 30 Color.Red |
Wzorzec identyfikatora | Wartość przypadku unii dyskryminowanej, etykiety wyjątku lub aktywnego przypadku wzorca | Some(x) Failure(msg) |
Wzorzec zmiennej | identifier | a |
as Wzór |
wzorzec jako identyfikator | (a, b) as tuple1 |
WZORZEC OR | pattern1 | pattern2 | ([h] | [h; _]) |
WZORZEC AND | wzorzec1 i wzorzec2 | (a, b) & (_, "test") |
Wzorzec wad | identyfikator :: list-identifier | h :: t |
Wzorzec listy | [ pattern_1; ... ; pattern_n ] | [ a; b; c ] |
Wzorzec tablicy | [| pattern_1; ..; pattern_n |] | [| a; b; c |] |
Wzorzec nawiasów | ( wzorzec ) | ( a ) |
Wzorzec krotki | ( pattern_1, ... , pattern_n ) | ( a, b ) |
Wzorzec rekordu | { identyfikator1 = pattern_1; ... ; = identifier_n pattern_n } | { Name = name; } |
Wzorzec z symbolami wieloznacznymi | _ | _ |
Wzorzec wraz z adnotacją typu | wzorzec : typ | a : int |
Wzorzec testu typu | :? type [ jako identyfikator ] | :? System.DateTime as dt |
Wzorzec o wartości null | null | null |
Wzorzec nameof | wyrażenie nameof | nameof str |
Wzorce stałe
Wzorce stałe to literały liczbowe, znakowe i ciągowe, stałe wyliczenia (z uwzględniną nazwą typu wyliczenia). Wyrażenie match
, które ma tylko stałe wzorce, można porównać z instrukcją case w innych językach. Dane wejściowe są porównywane z wartością literału, a wzorzec jest zgodny, jeśli wartości są równe. Typ literału musi być zgodny z typem danych wejściowych.
W poniższym przykładzie pokazano użycie wzorców literału, a także używa wzorca zmiennej i wzorca 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
Innym przykładem wzorca literału jest wzorzec oparty na stałych wyliczenia. Podczas używania stałych wyliczenia należy określić nazwę typu wyliczenia.
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
Wzorce identyfikatorów
Jeśli wzorzec jest ciągiem znaków, który stanowi prawidłowy identyfikator, formularz identyfikatora określa sposób dopasowania wzorca. Jeśli identyfikator jest dłuższy niż pojedynczy znak i zaczyna się od znaku wielkiej litery, kompilator próbuje dopasować element do wzorca identyfikatora. Identyfikator tego wzorca może być wartością oznaczoną atrybutem Literału, przypadkiem dyskryminowanym unii, identyfikatorem wyjątku lub aktywnym przypadkiem wzorca. Jeśli nie zostanie znaleziony pasujący identyfikator, dopasowanie zakończy się niepowodzeniem, a następna reguła wzorca, wzorzec zmiennej, zostanie porównany z danymi wejściowymi.
Wzorce unii dyskryminowanej mogą być prostymi nazwanymi przypadkami lub mogą mieć wartość lub krotkę zawierającą wiele wartości. Jeśli istnieje wartość, musisz określić identyfikator wartości. W przypadku krotki należy podać wzorzec krotki z identyfikatorem dla każdego elementu krotki lub identyfikatora o nazwie pola dla co najmniej jednego nazwanego pola unii. Zobacz przykłady kodu w tej sekcji, aby zapoznać się z przykładami.
Typ option
jest związkiem dyskryminowanym, który ma dwa przypadki, Some
i None
. Jeden przypadek () ma wartość, ale drugi (Some
None
) jest tylko nazwanym przypadkiem. Some
W związku z tym musi mieć zmienną dla wartości skojarzonej z przypadkiemSome
, ale None
musi być wyświetlana samodzielnie. W poniższym kodzie zmienna var1
otrzymuje wartość uzyskaną przez dopasowanie do Some
wielkości liter.
let printOption (data : int option) =
match data with
| Some var1 -> printfn "%d" var1
| None -> ()
W poniższym przykładzie związek PersonName
dyskryminowany zawiera kombinację ciągów i znaków reprezentujących możliwe formy nazw. Przypadki dyskryminowanego związku to FirstOnly
, LastOnly
i 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
W przypadku związków dyskryminowanych, które mają nazwane pola, należy użyć znaku równości (=), aby wyodrębnić wartość nazwanego pola. Rozważmy na przykład dyskryminowaną unię z deklaracją podobną do poniższej.
type Shape =
| Rectangle of height : float * width : float
| Circle of radius : float
Nazwane pola można użyć w wyrażeniu dopasowania wzorca w następujący sposób.
let matchShape shape =
match shape with
| Rectangle(height = h) -> printfn $"Rectangle with length %f{h}"
| Circle(r) -> printfn $"Circle with radius %f{r}"
Użycie nazwanego pola jest opcjonalne, więc w poprzednim przykładzie oba Circle(r)
elementy i Circle(radius = r)
mają ten sam efekt.
Podczas określania wielu pól należy użyć średnika (;) jako separatora.
match shape with
| Rectangle(height = h; width = w) -> printfn $"Rectangle with height %f{h} and width %f{w}"
| _ -> ()
Aktywne wzorce umożliwiają definiowanie bardziej złożonego dopasowania wzorca niestandardowego. Aby uzyskać więcej informacji na temat aktywnych wzorców, zobacz Aktywne wzorce.
Przypadek, w którym identyfikator jest wyjątkiem, jest używany we wzorcu pasującym w kontekście procedur obsługi wyjątków. Aby uzyskać informacje o dopasowywaniu wzorca w obsłudze wyjątków, zobacz Wyjątki: try...with
Wyrażenie.
Wzorce zmiennych
Wzorzec zmiennej przypisuje dopasowaną wartość do nazwy zmiennej, która jest następnie dostępna do użycia w wyrażeniu wykonywania z prawej strony symbolu ->
. Sam wzorzec zmiennej jest zgodny z dowolnymi danymi wejściowymi, ale wzorce zmiennych często pojawiają się w innych wzorcach, co umożliwia bardziej złożone struktury, takie jak krotki i tablice, które mają być rozłożone na zmienne.
W poniższym przykładzie pokazano wzorzec zmiennej we wzorcu krotki.
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 wzorzec
Wzorzec as
jest wzorcem, który ma dołączony do niego klauzulę as
. Klauzula as
wiąże dopasowaną wartość z nazwą, która może być używana w wyrażeniu match
wykonywania wyrażenia lub w przypadku, gdy ten wzorzec jest używany w powiązaniu let
, nazwa jest dodawana jako powiązanie do zakresu lokalnego.
W poniższym przykładzie użyto as
wzorca.
let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1
WZORZEC OR
Wzorzec OR jest używany, gdy dane wejściowe mogą być zgodne z wieloma wzorcami i chcesz wykonać ten sam kod w wyniku. Typy obu stron wzorca OR muszą być zgodne.
W poniższym przykładzie pokazano wzorzec 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)
WZORZEC AND
Wzorzec AND wymaga dopasowania danych wejściowych do dwóch wzorców. Typy obu stron wzorca AND muszą być zgodne.
Poniższy przykład jest podobny do detectZeroTuple
przedstawionego w sekcji Wzorzec krotki w dalszej części tego tematu, ale tutaj i var1
var2
są uzyskiwane jako wartości przy użyciu wzorca 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)
Wzorzec cons
Wzorzec cons służy do rozkładania listy do pierwszego elementu, głowy i listy zawierającej pozostałe elementy, ogon.
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
Wzorzec listy
Wzorzec listy umożliwia rozłożenie list na wiele elementów. Wzorzec listy może być zgodny tylko z listami określonej liczby elementów.
// 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 [ ] )
Wzorzec tablicy
Wzorzec tablicy przypomina wzorzec listy i może służyć do rozkładania tablic o określonej długości.
// 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 [| |] )
Wzorzec nawiasu
Nawiasy można grupować wokół wzorców, aby osiągnąć żądaną kojarzenie. W poniższym przykładzie nawiasy służą do kontrolowania asocjacji między wzorcem AND a wzorcem cons.
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
Wzorzec krotki
Wzorzec krotki pasuje do danych wejściowych w formularzu krotki i umożliwia rozłożenie krotki na elementy składowe przy użyciu zmiennych pasujących do wzorca dla każdej pozycji w krotki.
W poniższym przykładzie pokazano wzorzec krotki, a także używa wzorców literałów, wzorców zmiennych i wzorca symboli wieloznacznych.
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)
Wzorzec rekordu
Wzorzec rekordu służy do dekompilowania rekordów w celu wyodrębnienia wartości pól. Wzorzec nie musi odwoływać się do wszystkich pól rekordu; wszystkie pominięte pola po prostu nie uczestniczą w dopasowywaniu i nie są wyodrębniane.
// 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"
Wzorzec z symbolami wieloznacznymi
Wzorzec symbolu wieloznakowego jest reprezentowany przez znak podkreślenia (_
) i pasuje do wszystkich danych wejściowych, podobnie jak wzorzec zmiennej, z tą różnicą, że dane wejściowe są odrzucane zamiast przypisywane do zmiennej. Wzorzec symbolu wieloznacznego jest często używany w innych wzorcach jako symbol zastępczy dla wartości, które nie są potrzebne w wyrażeniu po prawej stronie symbolu ->
. Wzorzec z symbolami wieloznacznymi jest również często używany na końcu listy wzorców, aby dopasować je do wszelkich niezgodnych danych wejściowych. Wzorzec z symbolami wieloznacznymi przedstawiono w wielu przykładach kodu w tym temacie. Zobacz poprzedni kod, aby zapoznać się z jednym przykładem.
Wzorce, które mają adnotacje typu
Wzorce mogą mieć adnotacje typu. Zachowują się one jak inne adnotacje typu i wnioskowanie przewodnika, takie jak inne adnotacje typu. Nawiasy są wymagane wokół adnotacji typu we wzorcach. Poniższy kod przedstawia wzorzec z adnotacją typu.
let detect1 x =
match x with
| 1 -> printfn "Found a 1!"
| (var1 : int) -> printfn "%d" var1
detect1 0
detect1 1
Wzorzec testu typu
Wzorzec testu typu służy do dopasowywania danych wejściowych do typu. Jeśli typ danych wejściowych jest dopasowaniem (lub typem pochodnym) typu określonego we wzorcu, dopasowanie powiedzie się.
W poniższym przykładzie pokazano wzorzec 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."
| _ -> ()
Jeśli sprawdzasz tylko, czy identyfikator jest określonym typem pochodnym, nie potrzebujesz as identifier
części wzorca, jak pokazano w poniższym przykładzie:
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"
| _ -> ()
Wzorzec o wartości null
Wzorzec wartości null jest zgodny z wartością null, która może być wyświetlana podczas pracy z typami, które zezwalają na wartość null. Wzorce o wartości null są często używane podczas współdziałania z kodem programu .NET Framework. Na przykład zwracana wartość interfejsu API platformy .NET może być danymi wejściowymi match
wyrażenia. Przepływ programu można kontrolować na podstawie tego, czy wartość zwracana ma wartość null, a także inne cechy zwracanej wartości. Możesz użyć wzorca null, aby zapobiec propagacji wartości null do pozostałej części programu.
W poniższym przykładzie użyto wzorca null i wzorca zmiennej.
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()
Wzorzec nameof
Wzorzec nameof
jest zgodny z ciągiem, gdy jego wartość jest równa wyrażeniu, które jest zgodne ze nameof
słowem kluczowym. na przykład:
let f (str: string) =
match str with
| nameof str -> "It's 'str'!"
| _ -> "It is not 'str'!"
f "str" // matches
f "asdf" // does not match
Zobacz operator, nameof
aby uzyskać informacje o tym, co można nazwać.