Modèles actifs (F#)
Les modèles actifs vous permettent de définir des partitions nommées qui subdivisent des données d'entrée, ce qui vous permet d'utiliser ces noms dans une expression de critères spéciaux de la même façon qu'une union discriminée. Vous pouvez utiliser des modèles actifs pour décomposer des données de façon personnalisée pour chaque partition.
// Complete active pattern definition.
let (|identifer1|identifier2|...|) [ arguments ] = expression
// Partial active pattern definition.
let (|identifier|_|) [ arguments ] = expression
Notes
Dans la syntaxe précédente, les identificateurs sont des noms pour les partitions des données d'entrée représentées par arguments ou, en d'autres termes, des noms pour les sous-ensembles du jeu de toutes les valeurs des arguments. Il peut y avoir jusqu'à sept partitions dans une définition de modèle actif. expression décrit le mode de décomposition des données. Vous pouvez utiliser une définition de modèle actif pour définir les règles qui déterminent les partitions nommées auxquelles les valeurs données comme arguments appartiennent. Les symboles (| et |) sont appelés « banana clips », et la fonction créée par ce type de liaison let est appelée module de reconnaissance actif.
À titre d'exemple, considérez le modèle actif suivant avec un argument.
let (|Even|Odd|) input = if input % 2 = 0 then Even else Odd
Vous pouvez utiliser le modèle actif dans une expression de critères spéciaux, comme dans l'exemple suivant.
let TestNumber input =
match input with
| Even -> printfn "%d is even" input
| Odd -> printfn "%d is odd" input
TestNumber 7
TestNumber 11
TestNumber 32
La sortie de ce programme se présente comme suit :
7 is odd
11 is odd
32 is even
Les modèles actifs peuvent aussi servir à décomposer des types de données de plusieurs façons, par exemple lorsque les mêmes données sous-jacentes ont différentes représentations possible. Par exemple, un objet Color peut être décomposé en représentation RVB ou en représentation TSL.
open System.Drawing
let (|RGB|) (col : System.Drawing.Color) =
( col.R, col.G, col.B )
let (|HSB|) (col : System.Drawing.Color) =
( col.GetHue(), col.GetSaturation(), col.GetBrightness() )
let printRGB (col: System.Drawing.Color) =
match col with
| RGB(r, g, b) -> printfn " Red: %d Green: %d Blue: %d" r g b
let printHSB (col: System.Drawing.Color) =
match col with
| HSB(h, s, b) -> printfn " Hue: %f Saturation: %f Brightness: %f" h s b
let printAll col colorString =
printfn "%s" colorString
printRGB col
printHSB col
printAll Color.Red "Red"
printAll Color.Black "Black"
printAll Color.White "White"
printAll Color.Gray "Gray"
printAll Color.BlanchedAlmond "BlanchedAlmond"
La sortie du programme ci-dessus se présente comme suit :
Red
R: 255 G: 0 B: 0
H: 0.000000 S: 1.000000 B: 0.500000
Black
R: 0 G: 0 B: 0
H: 0.000000 S: 0.000000 B: 0.000000
White
R: 255 G: 255 B: 255
H: 0.000000 S: 0.000000 B: 1.000000
Gray
R: 128 G: 128 B: 128
H: 0.000000 S: 0.000000 B: 0.501961
BlanchedAlmond
R: 255 G: 235 B: 205
H: 36.000000 S: 1.000000 B: 0.901961
Une fois combinés, ces deux modes d'utilisation des modèles actifs vous permettent de partitionner et de décomposer des données sous une forme adéquate et d'effectuer les calculs appropriés sur les données appropriées dans la forme la plus pratique pour le calcul.
Les expressions de critères spéciaux résultantes offrent un moyen pratique d'écrire des données lisibles, simplifiant ainsi considérablement le code de branchement et d'analyse des données qui peut s'avérer complexe.
Modèles actifs partiels
Parfois, il vous faut uniquement partitionner une partie de l'espace d'entrée. Dans ce cas, vous écrivez un jeu de modèles partiels, chacun d'entre eux correspondant à certaines entrées mais pas à d'autres. Les modèles actifs qui ne produisent pas toujours de valeur sont appelés modèles actifs partiels ; ils ont une valeur de retour qui est un type d'option. Pour définir un modèle actif partiel, utilisez un caractère générique (_) à la fin de la liste des modèles à l'intérieur des « banana clips ». Le code suivant illustre l'utilisation d'un modèle actif partiel.
let (|Integer|_|) (str: string) =
let mutable intvalue = 0
if System.Int32.TryParse(str, &intvalue) then Some(intvalue)
else None
let (|Float|_|) (str: string) =
let mutable floatvalue = 0.0
if System.Double.TryParse(str, &floatvalue) then Some(floatvalue)
else None
let parseNumeric str =
match str with
| Integer i -> printfn "%d : Integer" i
| Float f -> printfn "%f : Floating point" f
| _ -> printfn "%s : Not matched." str
parseNumeric "1.1"
parseNumeric "0"
parseNumeric "0.0"
parseNumeric "10"
parseNumeric "Something else"
La sortie de l'exemple précédent est la suivante :
1.100000 : Floating point
0 : Integer
0.000000 : Floating point
10 : Integer
Something else : Not matched.
Lors de l'utilisation de modèles actifs partiels, les choix individuels peuvent parfois être disjoints ou mutuellement exclusifs, mais cela n'est pas nécessaire. Dans l'exemple suivant, le modèle Square et le modèle Cube ne sont pas disjoints, car certains nombres sont à la fois des carrés et des cubes, tels que 64. Le programme suivant imprime tous les entiers jusqu'à 1 000 000 qui sont à la fois des carrés et des cubes.
let err = 1.e-10
let isNearlyIntegral (x:float) = abs (x - round(x)) < err
let (|Square|_|) (x : int) =
if isNearlyIntegral (sqrt (float x)) then Some(x)
else None
let (|Cube|_|) (x : int) =
if isNearlyIntegral ((float x) ** ( 1.0 / 3.0)) then Some(x)
else None
let examineNumber x =
match x with
| Cube x -> printfn "%d is a cube" x
| _ -> ()
match x with
| Square x -> printfn "%d is a square" x
| _ -> ()
let findSquareCubes x =
if (match x with
| Cube x -> true
| _ -> false
&&
match x with
| Square x -> true
| _ -> false
)
then printf "%d \n" x
[ 1 .. 1000000 ] |> List.iter (fun elem -> findSquareCubes elem)
La sortie est la suivante :
1
64
729
4096
15625
46656
117649
262144
531441
1000000
Modèles actifs paramétrables
Les modèles actifs prennent toujours au moins un argument pour l'élément qui est mis en correspondance, mais ils peuvent également prendre des arguments supplémentaires, auquel cas le nom modèle actif paramétrable s'applique. Des arguments supplémentaires permettent de spécialiser un modèle général. Par exemple, des modèles actifs qui utilisent des expressions régulières pour analyser des chaînes incluent souvent l'expression régulière comme paramètre supplémentaire, comme dans le code suivant, qui utilise également le modèle actif partiel Integer défini dans l'exemple de code précédent. Dans cet exemple, les chaînes qui utilisent des expressions régulières pour différents formats de date sont données pour personnaliser le modèle actif ParseRegex général. Le modèle actif Integer est utilisé pour convertir les chaînes mises en correspondance en entiers qui peuvent être passés au constructeur DateTime.
open System.Text.RegularExpressions
// ParseRegex parses a regular expression and returns a list of the strings that match each group in
// the regular expression.
// List.tail is called to eliminate the first element in the list, which is the full matched expression,
// since only the matches for each group are wanted.
let (|ParseRegex|_|) regex str =
let m = Regex(regex).Match(str)
if m.Success
then Some (List.tail [ for x in m.Groups -> x.Value ])
else None
// Three different date formats are demonstrated here. The first matches two-
// digit dates and the second matches full dates. This code assumes that if a two-digit
// date is provided, it is an abbreviation, not a year in the first century.
let parseDate str =
match str with
| ParseRegex "(\d{1,2})/(\d{1,2})/(\d{1,2})$" [Integer m; Integer d; Integer y]
-> new System.DateTime(y + 2000, m, d)
| ParseRegex "(\d{1,2})/(\d{1,2})/(\d{3,4})" [Integer m; Integer d; Integer y]
-> new System.DateTime(y, m, d)
| ParseRegex "(\d{1,4})-(\d{1,2})-(\d{1,2})" [Integer y; Integer m; Integer d]
-> new System.DateTime(y, m, d)
| _ -> new System.DateTime()
let dt1 = parseDate "12/22/08"
let dt2 = parseDate "1/1/2009"
let dt3 = parseDate "2008-1-15"
let dt4 = parseDate "1995-12-28"
printfn "%s %s %s %s" (dt1.ToString()) (dt2.ToString()) (dt3.ToString()) (dt4.ToString())
La sortie du code précédent est la suivante :
12/22/2008 12:00:00 AM 1/1/2009 12:00:00 AM 1/15/2008 12:00:00 AM 12/28/1995 12:00:00 AM