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.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow