Suche…
Einfache aktive Muster
Aktive Muster sind eine spezielle Art der Mustererkennung, in der Sie benannte Kategorien angeben können, in die Ihre Daten fallen können, und diese Kategorien dann in match
.
So definieren Sie ein aktives Muster, das Zahlen als positiv, negativ oder null klassifiziert:
let (|Positive|Negative|Zero|) num =
if num > 0 then Positive
elif num < 0 then Negative
else Zero
Dies kann dann in einem Mustervergleichsausdruck verwendet werden:
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
Aktive Muster mit Parametern
Aktive Muster sind nur einfache Funktionen.
Wie Funktionen können Sie zusätzliche Parameter definieren:
let (|HasExtension|_|) expected (uri : string) =
let result = uri.EndsWith (expected, StringComparison.CurrentCultureIgnoreCase)
match result with
| true -> Some true
| _ -> None
Dies kann in einem Muster verwendet werden, das auf diese Weise passt:
let isXMLFile uri =
match uri with
| HasExtension ".xml" _ -> true
| _ -> false
Aktive Muster können zur Überprüfung und Umwandlung von Funktionsargumenten verwendet werden
Eine interessante, aber eher unbekannte Verwendung von Active Patterns in F#
besteht darin, dass sie zum Validieren und Transformieren von Funktionsargumenten verwendet werden können.
Betrachten Sie die klassische Art der Argumentvalidierung:
// 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 ()
Normalerweise fügen wir der Methode Code hinzu, um zu überprüfen, ob die Argumente korrekt sind. Mit Active Patterns in F#
wir dies verallgemeinern und die Absicht in der Argumentdeklaration deklarieren.
Der folgende Code entspricht dem obigen Code:
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 ()
Für den Benutzer der Funktion f
und g
gibt es keinen Unterschied zwischen den beiden verschiedenen Versionen.
printfn "%A" <| f (Some "Test") None // Prints "Test There"
printfn "%A" <| g "Test" // Prints "Test"
printfn "%A" <| g null // Will throw
Ein Problem ist, wenn Active Patterns Performance-Overhead hinzufügen. Verwenden wir ILSpy
um f
und g
zu dekompilieren, um zu sehen, ob dies der Fall ist.
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();
}
Dank inline
fügen die Active Patterns keinen zusätzlichen Overhead hinzu, verglichen mit der klassischen Methode der Argumentvalidierung.
Aktive Muster als .NET-API-Wrapper
Active Patterns können verwendet werden, um das Aufrufen einiger .NET-APIs natürlicher zu machen, insbesondere solche, die einen Ausgabeparameter verwenden, um mehr als nur den Funktionsrückgabewert zurückzugeben.
Beispielsweise würden Sie normalerweise die Methode System.Int32.TryParse
wie folgt System.Int32.TryParse
:
let couldParse, parsedInt = System.Int32.TryParse("1")
if couldParse then printfn "Successfully parsed int: %i" parsedInt
else printfn "Could not parse int"
Sie können dies mit dem Pattern-Matching verbessern:
match System.Int32.TryParse("1") with
| (true, parsedInt) -> printfn "Successfully parsed int: %i" parsedInt
| (false, _) -> printfn "Could not parse int"
Wir können jedoch auch das folgende aktive Muster definieren, das die Funktion System.Int32.TryParse
:
let (|Int|_|) str =
match System.Int32.TryParse(str) with
| (true, parsedInt) -> Some parsedInt
| _ -> None
Jetzt können wir folgendes tun:
match "1" with
| Int parsedInt -> printfn "Successfully parsed int: %i" parsedInt
| _ -> printfn "Could not parse int"
Ein weiterer guter Kandidat für die Umhüllung in aktive Muster sind die APIs für reguläre Ausdrücke:
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"
Vollständige und teilweise aktive Muster
Es gibt zwei Arten von aktiven Mustern, die sich in der Verwendung etwas unterscheiden - vollständig und teilweise.
Vollständige aktive Muster können verwendet werden, wenn Sie alle Ergebnisse auflisten können, z. B. "Ist eine Zahl ungerade oder gerade?"
let (|Odd|Even|) number =
if number % 2 = 0
then Even
else Odd
Beachten Sie, dass die Definition des aktiven Musters sowohl mögliche Fälle als auch nichts anderes aufführt, und der Hauptteil gibt einen der aufgeführten Fälle zurück. Wenn Sie es in einem Übereinstimmungsausdruck verwenden, ist dies eine vollständige Übereinstimmung:
let n = 13
match n with
| Odd -> printf "%i is odd" n
| Even -> printf "%i is even" n
Dies ist praktisch, wenn Sie den Eingabebereich in bekannte Kategorien unterteilen möchten, die ihn vollständig abdecken.
Partial Active Patterns hingegen lassen Sie mögliche Ergebnisse explizit ignorieren, indem Sie eine option
. Ihre Definition verwendet einen Sonderfall von _
für den nicht übereinstimmenden Fall.
let (|Integer|_|) str =
match Int32.TryParse(str) with
| (true, i) -> Some i
| _ -> None
Auf diese Weise können wir auch dann übereinstimmen, wenn einige Fälle von unserer Parsing-Funktion nicht behandelt werden können.
let s = "13"
match s with
| Integer i -> "%i was successfully parsed!" i
| _ -> "%s is not an int" s
Teilweise aktive Muster können als eine Form des Testens verwendet werden, ob die Eingabe in eine bestimmte Kategorie im Eingabebereich fällt, während andere Optionen ignoriert werden.