Partager via


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 (|identifier1|identifier2|...|_|) [ 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 floatequal x y =
   abs (x - y) < err

let (|Square|_|) (x : int) =
  if floatequal (sqrt (float x)) (round (sqrt (float x))) then Some(x)
  else None

let (|Cube|_|) (x : int) =
  if floatequal ((float x) ** ( 1.0 / 3.0)) (round ((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 donnent 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

Voir aussi

Référence

Expressions match (F#)

Autres ressources

Référence du langage F#