Udostępnij za pośrednictwem


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 matchwzorzec 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", , 30Color.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 (SomeNone) 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, LastOnlyi 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 var1var2 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ć.

Zobacz też