Recherche…
Modèles actifs simples
Les modèles actifs sont un type spécial de correspondance de modèle dans lequel vous pouvez spécifier les catégories nommées dans lesquelles vos données peuvent tomber, puis utiliser ces catégories dans les instructions de match
.
Pour définir un modèle actif qui classe les nombres comme positifs, négatifs ou nuls:
let (|Positive|Negative|Zero|) num =
if num > 0 then Positive
elif num < 0 then Negative
else Zero
Cela peut ensuite être utilisé dans une expression de correspondance de motif:
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
Modèles actifs avec paramètres
Les modèles actifs ne sont que des fonctions simples.
Comme les fonctions, vous pouvez définir des paramètres supplémentaires:
let (|HasExtension|_|) expected (uri : string) =
let result = uri.EndsWith (expected, StringComparison.CurrentCultureIgnoreCase)
match result with
| true -> Some true
| _ -> None
Cela peut être utilisé dans un modèle correspondant à cette manière:
let isXMLFile uri =
match uri with
| HasExtension ".xml" _ -> true
| _ -> false
Les patterns actifs peuvent être utilisés pour valider et transformer des arguments de fonction
Une utilisation intéressante mais plutôt inconnue des modèles actifs dans F#
est qu'ils peuvent être utilisés pour valider et transformer des arguments de fonction.
Considérons la méthode classique de validation des arguments:
// 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 ()
En règle générale, nous ajoutons du code dans la méthode pour vérifier que les arguments sont corrects. En utilisant les modèles actifs dans F#
nous pouvons généraliser ceci et déclarer l'intention dans la déclaration d'argument.
Le code suivant est équivalent au code ci-dessus:
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 ()
Pour l'utilisateur de la fonction f
et g
il n'y a pas de différence entre les deux versions.
printfn "%A" <| f (Some "Test") None // Prints "Test There"
printfn "%A" <| g "Test" // Prints "Test"
printfn "%A" <| g null // Will throw
Une préoccupation est de savoir si Active Patterns ajoute des performances supplémentaires. Utilisons ILSpy
pour décompiler f
et g
pour voir si c'est le cas.
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();
}
Grâce à inline
Active Patterns n’ajoute aucune surcharge supplémentaire par rapport à la méthode classique de validation des arguments.
Modèles actifs comme wrappers API .NET
Les patterns actifs peuvent être utilisés pour rendre l'appel de certaines API .NET plus naturel, en particulier ceux qui utilisent un paramètre de sortie pour renvoyer plus que la valeur de retour de la fonction.
Par exemple, vous appelez normalement la méthode System.Int32.TryParse
comme suit:
let couldParse, parsedInt = System.Int32.TryParse("1")
if couldParse then printfn "Successfully parsed int: %i" parsedInt
else printfn "Could not parse int"
Vous pouvez améliorer ceci un peu en utilisant la correspondance de motif:
match System.Int32.TryParse("1") with
| (true, parsedInt) -> printfn "Successfully parsed int: %i" parsedInt
| (false, _) -> printfn "Could not parse int"
Cependant, nous pouvons également définir le modèle actif suivant qui encapsule la fonction System.Int32.TryParse
:
let (|Int|_|) str =
match System.Int32.TryParse(str) with
| (true, parsedInt) -> Some parsedInt
| _ -> None
Maintenant, nous pouvons faire ce qui suit:
match "1" with
| Int parsedInt -> printfn "Successfully parsed int: %i" parsedInt
| _ -> printfn "Could not parse int"
Les API d'expressions régulières sont un autre bon candidat pour être inclus dans un modèle actif:
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"
Modèles actifs complets et partiels
Il existe deux types de motifs actifs dont l'utilisation est quelque peu différente: complète et partielle.
Des modèles actifs complets peuvent être utilisés lorsque vous pouvez énumérer tous les résultats, comme "est-ce qu'un nombre impair ou pair?"
let (|Odd|Even|) number =
if number % 2 = 0
then Even
else Odd
Notez que la définition de modèle actif répertorie à la fois les cas possibles et rien d'autre et que le corps renvoie l'un des cas répertoriés. Lorsque vous l'utilisez dans une expression de correspondance, il s'agit d'une correspondance complète:
let n = 13
match n with
| Odd -> printf "%i is odd" n
| Even -> printf "%i is even" n
Ceci est pratique lorsque vous voulez décomposer l'espace de saisie en catégories connues qui le couvrent complètement.
En revanche, les patrons actifs partiels vous permettent d'ignorer explicitement certains résultats possibles en renvoyant une option
. Leur définition utilise un cas particulier de _
pour le cas sans correspondance.
let (|Integer|_|) str =
match Int32.TryParse(str) with
| (true, i) -> Some i
| _ -> None
De cette façon, nous pouvons égaler même si certains cas ne peuvent pas être traités par notre fonction d'analyse.
let s = "13"
match s with
| Integer i -> "%i was successfully parsed!" i
| _ -> "%s is not an int" s
Les modèles actifs partiels peuvent être utilisés comme une forme de test, que l'entrée entre ou non dans une catégorie spécifique dans l'espace d'entrée, tout en ignorant les autres options.