Szukaj…


Proste aktywne wzory

Aktywne wzorce to specjalny rodzaj dopasowywania wzorców, w którym można określić nazwane kategorie, do których mogą należeć dane, a następnie użyć tych kategorii w instrukcjach match .

Aby zdefiniować aktywny wzorzec, który klasyfikuje liczby jako dodatnie, ujemne lub zerowe:

let (|Positive|Negative|Zero|) num = 
    if num > 0 then Positive 
    elif num < 0 then Negative
    else Zero

Można to następnie zastosować w wyrażeniu dopasowującym wzór:

let Sign value = 
    match value with
    | Positive -> printf "%d is positive" value
    | Negative -> printf "%d is negative" value
    | Zero -> printf "The value is zero"

Sign -19 // -19 is negative
Sign 2 // 2 is positive
Sign 0 // The value is zero

Aktywne wzorce z parametrami

Aktywne wzorce to tylko proste funkcje.

Podobnie jak funkcje możesz zdefiniować dodatkowe parametry:

let (|HasExtension|_|) expected (uri : string) = 
    let result = uri.EndsWith (expected, StringComparison.CurrentCultureIgnoreCase)
    match result with
    | true -> Some true
    | _ -> None

Można tego użyć we wzorcu pasującym w ten sposób:

    let isXMLFile uri =
        match uri with
        | HasExtension ".xml" _ -> true
        | _ -> false

Aktywnych wzorców można używać do sprawdzania poprawności i transformacji argumentów funkcji

Ciekawym, ale raczej nieznanym zastosowaniem Active Patterns w F# jest to, że można ich używać do sprawdzania poprawności i transformacji argumentów funkcji.

Rozważ klasyczny sposób sprawdzania poprawności argumentów:

// val f : string option -> string option -> string
let f v u =
  let v = defaultArg v "Hello"
  let u = defaultArg u "There"
  v + " " + u

// val g : 'T -> 'T (requires 'T null)
let g v =
  match v with
  | null  -> raise (System.NullReferenceException ())
  | _     -> v.ToString ()

Zazwyczaj dodajemy kod do metody, aby sprawdzić, czy argumenty są poprawne. Używając Active Patterns w F# możemy to uogólnić i zadeklarować zamiar w deklaracji argumentu.

Poniższy kod jest równoważny powyższemu kodowi:

let inline (|DefaultArg|) dv ov = defaultArg ov dv

let inline (|NotNull|) v =
  match v with
  | null  -> raise (System.NullReferenceException ())
  | _     -> v

// val f : string option -> string option -> string
let f (DefaultArg "Hello" v) (DefaultArg "There" u) = v + " " + u

// val g : 'T -> string (requires 'T null)
let g (NotNull v) = v.ToString ()

Dla użytkownika funkcji f i g nie ma różnicy między dwiema różnymi wersjami.

printfn "%A" <| f (Some "Test") None  // Prints "Test There"
printfn "%A" <| g "Test"              // Prints "Test"
printfn "%A" <| g null                // Will throw

Niepokojące jest to, czy aktywne wzorce zwiększają wydajność. Użyjmy ILSpy do dekompilacji f i g aby sprawdzić, czy tak jest.

public static string f(FSharpOption<string> _arg2, FSharpOption<string> _arg1)
{
  return Operators.DefaultArg<string>(_arg2, "Hello") + " " + Operators.DefaultArg<string>(_arg1, "There");
}

public static string g<a>(a _arg1) where a : class
{
  if (_arg1 != null)
  {
    a a = _arg1;
    return a.ToString();
  }
  throw new NullReferenceException();
}

Dzięki inline Active Patterns nie dodaje dodatkowego obciążenia w porównaniu z klasycznym sposobem sprawdzania poprawności argumentów.

Aktywne wzorce jako opakowania API .NET

Aktywne wzorce mogą być używane, aby wywoływanie niektórych interfejsów API platformy .NET wydawało się bardziej naturalne, szczególnie tych, które używają parametru wyjściowego do zwracania więcej niż tylko zwracanej wartości funkcji.

Na przykład normalnie wywołujesz metodę System.Int32.TryParse w następujący sposób:

let couldParse, parsedInt = System.Int32.TryParse("1")
if couldParse then printfn "Successfully parsed int: %i" parsedInt
else printfn "Could not parse int"

Możesz to nieco poprawić, używając dopasowania wzorca:

match System.Int32.TryParse("1") with 
| (true, parsedInt) -> printfn "Successfully parsed int: %i" parsedInt
| (false, _) -> printfn "Could not parse int"

Możemy jednak również zdefiniować następujący aktywny wzorzec, który otacza funkcję System.Int32.TryParse :

let (|Int|_|) str =
    match System.Int32.TryParse(str) with
    | (true, parsedInt) -> Some parsedInt
    | _ -> None

Teraz możemy wykonać następujące czynności:

match "1" with
| Int parsedInt -> printfn "Successfully parsed int: %i" parsedInt
| _ -> printfn "Could not parse int"

Innym dobrym kandydatem do zawinięcia w aktywne wzorce są interfejsy API wyrażeń regularnych:

let (|MatchRegex|_|) pattern input =
    let m = System.Text.RegularExpressions.Regex.Match(input, pattern) 
    if m.Success then Some m.Groups.[1].Value 
    else None  

match "bad" with
| MatchRegex "(good|great)" mood -> 
    printfn "Wow, it's a %s day!" mood
| MatchRegex "(bad|terrible)" mood -> 
    printfn "Unfortunately, it's a %s day." mood
| _ -> 
    printfn "Just a normal day"

Kompletne i częściowo aktywne wzorce

Istnieją dwa rodzaje aktywnych wzorców, które nieco różnią się w użyciu - kompletne i częściowe.

Kompletnych aktywnych wzorców można użyć, gdy jesteś w stanie wyliczyć wszystkie wyniki, np. „Czy liczba jest nieparzysta czy parzysta?”

let (|Odd|Even|) number = 
  if number % 2 = 0
  then Even
  else Odd

Zauważ, że definicja aktywnego wzorca zawiera zarówno możliwe przypadki, jak i nic więcej, a treść zwraca jeden z wymienionych przypadków. Gdy użyjesz go w wyrażeniu dopasowania, jest to pełne dopasowanie:

let n = 13
match n with
| Odd -> printf "%i is odd" n
| Even -> printf "%i is even" n

Jest to przydatne, gdy chcesz podzielić przestrzeń wejściową na znane kategorie, które całkowicie ją obejmują.

Z drugiej strony częściowe aktywne wzorce pozwalają jawnie zignorować niektóre możliwe wyniki, zwracając option . W ich definicji użyto specjalnego przypadku _ dla niedopasowanego przypadku.

let (|Integer|_|) str =
  match Int32.TryParse(str) with
  | (true, i) -> Some i
  | _ -> None

W ten sposób możemy dopasować, nawet jeśli niektóre przypadki nie mogą być obsługiwane przez naszą funkcję parsowania.

let s = "13"
match s with
| Integer i -> "%i was successfully parsed!" i
| _ -> "%s is not an int" s

Częściowe aktywne wzorce można wykorzystać jako formę testowania, czy dane wejściowe należą do określonej kategorii w przestrzeni wejściowej, ignorując inne opcje.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow