Critères spéciaux (F#)
Les modèles sont des règles de transformation des données d'entrée. Elles sont utilisées dans tout le langage F# pour comparer des données à une ou plusieurs structures logiques, décomposer des données en parties constituantes ou extraire des informations des données de plusieurs façons.
Notes
Les modèles sont utilisés dans de nombreuses constructions de langage, par exemple l'expression match. Ils sont utilisés lorsque vous traitez des arguments pour des fonctions dans des liaisons let, des expressions lambda et des gestionnaires d'exceptions associés à l'expression try...with. Pour plus d'informations, consultez Expressions match (F#), Liaisons let (F#), Expressions lambda : mot clé fun (F#) et Exceptions : expression try...with (F#).
Par exemple, dans l'expression match, le pattern vient après le symbole de barre verticale.
match expression with
| pattern [ when condition ] -> result-expression
...
Chaque modèle joue le rôle d'une règle qui transforme l'entrée d'une certaine façon. Dans l'expression match, chaque modèle est examiné tour à tour pour déterminer si les données d'entrée sont compatibles avec le modèle. Si une correspondance est trouvée, l'expression de résultat est exécutée. Si aucune correspondance n'est trouvée, la règle de modèle suivante est testée. La partie facultative when condition est expliquée dans Expressions match (F#).
Les modèles pris en charge sont répertoriés dans le tableau suivant. Au moment de l'exécution, l'entrée est testée par rapport à chacun des modèles suivants dans l'ordre indiqué dans le tableau. Les modèles sont appliqués de manière récursive, du premier au dernier dans l'ordre dans lequel ils apparaissent dans votre code, et de gauche à droite pour les modèles qui apparaissent sur une ligne.
Nom |
Description |
Exemple |
---|---|---|
Modèle de constante |
Littéral numérique, de caractère ou de chaîne, constante d'énumération ou identificateur littéral défini |
1.0, "test", 30, Color.Red |
Modèle d'identificateur |
Valeur de cas d'une union discriminée, étiquette d'exception ou cas de modèle actif |
Some(x) Failure(msg) |
Modèle de variable |
identifier |
a |
Modèle as |
pattern as identifier |
(a, b) as tuple1 |
Modèle OR |
pattern1 | pattern2 |
([h] | [h; _]) |
Modèle AND |
pattern1 & pattern2 |
(a, b) & (_, "test") |
Modèle Cons |
identifier :: list-identifier |
h :: t |
Modèle de liste |
[ pattern_1; ... ; pattern_n ] |
[ a; b; c ] |
Modèle de tableau |
[| pattern_1; ..; pattern_n ] |
[| a; b; c |] |
Modèle entre parenthèses |
( pattern ) |
( a ) |
Modèle de tuple |
( pattern_1, ... , pattern_n ) |
( a, b ) |
Modèle d'enregistrement |
{ identifier1 = pattern_1; ... ; identifier_n = pattern_n } |
{ Name = name; } |
Modèle de caractère générique |
_ |
_ |
Modèle avec une annotation de type |
pattern : type |
a : int |
Modèle de test de type |
:? type [ as identifier ] |
:? System.DateTime as dt |
Modèle Null |
null |
null |
Modèles de constante
Les modèles de constante sont des littéraux numériques, de caractère et de chaîne, des constantes d'énumération (avec le nom du type d'énumération inclus). Une expression match qui possède uniquement des modèles de constante peut être comparée à une instruction case dans d'autres langages. L'entrée est comparée à la valeur littérale et le modèle effectue la mise en correspondance si les valeurs sont égales. Le type du littéral doit être compatible avec le type de l'entrée.
L'exemple suivant illustre l'utilisation de modèles de littéral, et utilise également un modèle de variable et un modèle OR.
[<Literal>]
let Three = 3
let filter123 x =
match x with
// The following line contains literal patterns combined with an OR pattern.
| 1 | 2 | Three -> printfn "Found 1, 2, or 3!"
// The following line contains a variable pattern.
| var1 -> printfn "%d" var1
for x in 1..10 do filter123 x
Un autre exemple de modèle de littéral est un modèle basé sur des constantes d'énumération. Vous devez spécifier le nom du type d'énumération lorsque vous utilisez des constantes d'énumération.
type Color =
| Red = 0
| Green = 1
| Blue = 2
let printColorName (color:Color) =
match color with
| Color.Red -> printfn "Red"
| Color.Green -> printfn "Green"
| Color.Blue -> printfn "Blue"
| _ -> ()
printColorName Color.Red
printColorName Color.Green
printColorName Color.Blue
Modèles d'identificateur
Si le modèle est une chaîne de caractères qui forment un identificateur valide, la forme de l'identificateur détermine comment la correspondance avec le modèle est établie. Si l'identificateur est plus long qu'un caractère unique et commence par une majuscule, le compilateur tente d'établir une correspondance avec le modèle d'identificateur. L'identificateur de ce modèle peut être une valeur marquée avec l'attribut littéral, un cas d'union discriminée, un identificateur d'exception ou un cas de modèle actif. Si aucun identificateur correspondant n'est trouvé, la correspondance échoue et la règle de modèle suivante, le modèle de variable, est comparée à l'entrée.
Les modèles d'union discriminée peuvent être des cas nommés simples ou peuvent avoir une valeur ou un tuple contenant plusieurs valeurs. S'il existe une valeur, vous devez spécifier un identificateur pour la valeur. En cas de tuple, vous devez fournir un modèle de tuple avec un identificateur pour chaque élément du tuple ou un identificateur avec un nom de champ pour un ou plusieurs champs d'union nommés. Consultez les exemples de code dans cette section pour obtenir des exemples.
Le type option est une union discriminée qui a deux cas, Some et None. Un cas (Some) a une valeur, mais l'autre cas (None) est simplement un cas nommé. Par conséquent, Some doit avoir une variable pour la valeur associée au cas Some, mais None doit apparaître seul. Dans le code suivant, la variable var1 se voit attribuer la valeur obtenue en la mettant en correspondance avec le cas Some.
let printOption (data : int option) =
match data with
| Some var1 -> printfn "%d" var1
| None -> ()
Dans l'exemple suivant, l'union discriminée PersonName contient un mélange de chaînes et de caractères qui représentent différentes formes possibles de noms. Les cas de l'union discriminée sont FirstOnly, LastOnly et FirstLast.
type PersonName =
| FirstOnly of string
| LastOnly of string
| FirstLast of string * string
let constructQuery personName =
match personName with
| FirstOnly(firstName) -> printf "May I call you %s?" firstName
| LastOnly(lastName) -> printf "Are you Mr. or Ms. %s?" lastName
| FirstLast(firstName, lastName) -> printf "Are you %s %s?" firstName lastName
Pour les unions discriminées dont les champs sont nommés, vous utilisez le signe égal (=) pour extraire la valeur d'un champ nommé. Par exemple, prenons une union discriminée avec la déclaration suivante.
type Shape =
| Rectangle of height : float * width : float
| Circle of radius : float
Vous pouvez utiliser les champs nommés dans une expression de critères spéciaux comme suit.
let matchShape shape =
match shape with
| Rectangle(height = h) -> printfn "Rectangle with length %f" h
| Circle(r) -> printfn "Circle with radius %f" r
L'utilisation du champ nommé est facultative, donc dans l'exemple précédent, Circle(r) et Circle(radius = r) ont le même effet.
Lorsque vous spécifiez plusieurs champs, utilisez le point-virgule (;) comme séparateur.
match shape with
| Rectangle(height = h; width = w) -> printfn "Rectangle with height %f and width %f" h w
| _ -> ()
Les modèles actifs vous permettent de définir des critères spéciaux personnalisés plus complexes. Pour plus d'informations sur les modèles actifs, consultez Modèles actifs (F#).
Le cas dans lequel l'identificateur est une exception est utilisé dans les critères spéciaux dans le contexte de gestionnaires d'exceptions. Pour plus d'informations sur les critères spéciaux dans la gestion des exceptions, consultez Exceptions : expression try...with (F#).
Modèles de variable
Le modèle de variable assigne la valeur mise en correspondance à un nom de variable, qui est ensuite disponible dans l'expression d'exécution à droite du symbole ->. Un modèle de variable seul met en correspondance toutes les entrées, mais les modèles de variable apparaissent souvent dans d'autres modèles, ce qui permet la décomposition de structures plus complexes (telles que des tuples et des tableaux) en variables.
L'exemple suivant illustre un modèle de variable dans un modèle de tuple.
let function1 x =
match x with
| (var1, var2) when var1 > var2 -> printfn "%d is greater than %d" var1 var2
| (var1, var2) when var1 < var2 -> printfn "%d is less than %d" var1 var2
| (var1, var2) -> printfn "%d equals %d" var1 var2
function1 (1,2)
function1 (2, 1)
function1 (0, 0)
Modèle as
Le modèle as est un modèle auquel une clause as a été ajoutée. La clause as lie la valeur correspondante à un nom qui peut être utilisé dans l'expression d'exécution d'une expression match ; ou, au cas où ce modèle serait utilisé dans une liaison let, le nom est ajouté en tant que liaison à la portée locale.
L'exemple suivant utilise un modèle as :
let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1
Modèle OR
Le modèle OR est utilisé si des données d'entrée peuvent correspondre à plusieurs modèles et que vous voulez exécuter le même code en tant que résultat. Les types des deux côtés du modèle OR doivent être compatibles.
L'exemple suivant illustre le modèle OR.
let detectZeroOR point =
match point with
| (0, 0) | (0, _) | (_, 0) -> printfn "Zero found."
| _ -> printfn "Both nonzero."
detectZeroOR (0, 0)
detectZeroOR (1, 0)
detectZeroOR (0, 10)
detectZeroOR (10, 15)
Modèle AND
Le modèle AND requiert que l'entrée corresponde à deux modèles. Les types des deux côtés du modèle AND doivent être compatibles.
L'exemple suivant est semblable à l'exemple detectZeroTuple présenté dans la section Modèle de tuple plus loin dans cette rubrique, mais ici, var1 et var2 sont obtenus en tant que valeurs à l'aide du modèle AND.
let detectZeroAND point =
match point with
| (0, 0) -> printfn "Both values zero."
| (var1, var2) & (0, _) -> printfn "First value is 0 in (%d, %d)" var1 var2
| (var1, var2) & (_, 0) -> printfn "Second value is 0 in (%d, %d)" var1 var2
| _ -> printfn "Both nonzero."
detectZeroAND (0, 0)
detectZeroAND (1, 0)
detectZeroAND (0, 10)
detectZeroAND (10, 15)
Modèle Cons
Le modèle Cons est utilisé pour décomposer une liste comme suit : le premier élément (le début) et une liste contenant les éléments restants (la fin).
let list1 = [ 1; 2; 3; 4 ]
// This example uses a cons pattern and a list pattern.
let rec printList l =
match l with
| head :: tail -> printf "%d " head; printList tail
| [] -> printfn ""
printList list1
Modèle de liste
Le modèle de liste permet la décomposition de listes en plusieurs éléments. Le modèle de liste proprement dit peut uniquement établir des correspondances avec des listes contenant un nombre spécifique d'éléments.
// This example uses a list pattern.
let listLength list =
match list with
| [] -> 0
| [ _ ] -> 1
| [ _; _ ] -> 2
| [ _; _; _ ] -> 3
| _ -> List.length list
printfn "%d" (listLength [ 1 ])
printfn "%d" (listLength [ 1; 1 ])
printfn "%d" (listLength [ 1; 1; 1; ])
printfn "%d" (listLength [ ] )
Modèle de tableau
Le modèle de tableau ressemble au modèle de liste et peut être utilisé pour décomposer des tableaux de longueur spécifique.
// This example uses array patterns.
let vectorLength vec =
match vec with
| [| var1 |] -> var1
| [| var1; var2 |] -> sqrt (var1*var1 + var2*var2)
| [| var1; var2; var3 |] -> sqrt (var1*var1 + var2*var2 + var3*var3)
| _ -> failwith "vectorLength called with an unsupported array size of %d." (vec.Length)
printfn "%f" (vectorLength [| 1. |])
printfn "%f" (vectorLength [| 1.; 1. |])
printfn "%f" (vectorLength [| 1.; 1.; 1.; |])
printfn "%f" (vectorLength [| |] )
Modèle entre parenthèses
Les parenthèses peuvent être groupées autour de modèles pour obtenir l'associativité désirée. Dans l'exemple suivant, des parenthèses sont utilisées pour contrôler l'associativité entre un modèle AND et un modèle Cons.
let countValues list value =
let rec checkList list acc =
match list with
| (elem1 & head) :: tail when elem1 = value -> checkList tail (acc + 1)
| head :: tail -> checkList tail acc
| [] -> acc
checkList list 0
let result = countValues [ for x in -10..10 -> x*x - 4 ] 0
printfn "%d" result
Modèle de tuple
Le modèle de tuple met en correspondance les entrées sous forme de tuple et permet la décomposition du tuple en éléments constituants à l'aide de variables de critères spéciaux pour chaque position dans le tuple.
L'exemple suivant illustre le modèle de tuple et utilise également des modèles de littéral, des modèles de variable et le modèle de caractère générique.
let detectZeroTuple point =
match point with
| (0, 0) -> printfn "Both values zero."
| (0, var2) -> printfn "First value is 0 in (0, %d)" var2
| (var1, 0) -> printfn "Second value is 0 in (%d, 0)" var1
| _ -> printfn "Both nonzero."
detectZeroTuple (0, 0)
detectZeroTuple (1, 0)
detectZeroTuple (0, 10)
detectZeroTuple (10, 15)
Modèle d'enregistrement
Le modèle d'enregistrement est utilisé pour décomposer des enregistrements pour extraire les valeurs de champs. Le modèle ne doit pas nécessairement référencer tous les champs de l'enregistrement ; tout champ omis ne participe pas à la mise en correspondance et n'est pas extrait.
// This example uses a record pattern.
type MyRecord = { Name: string; ID: int }
let IsMatchByName record1 (name: string) =
match record1 with
| { MyRecord.Name = nameFound; MyRecord.ID = _; } when nameFound = name -> true
| _ -> false
let recordX = { Name = "Parker"; ID = 10 }
let isMatched1 = IsMatchByName recordX "Parker"
let isMatched2 = IsMatchByName recordX "Hartono"
Modèle de caractère générique
Les caractères génériques sont représentés par le caractère de trait de soulignement (_) et correspondant à n'importe quelle entrée, comme le modèle de variable, mais l'entrée est ignorée au lieu d'être assignée à une variable. Le modèle de caractère générique est souvent utilisé dans d'autres modèles comme un espace réservé pour les valeurs qui ne sont pas requises dans l'expression à droite du symbole ->. Le modèle de caractère générique est aussi fréquemment utilisé à la fin d'une liste de modèles pour faire correspondre toutes les entrées sans correspondance. Le modèle de caractère générique est illustré dans de nombreux exemples de code dans cette rubrique. Consultez le code précédent pour obtenir un exemple.
Modèles avec des annotations de type
Les modèles peuvent avoir des annotations de type. Celles-ci se comportent comme d'autres annotations de type et guident l'inférence comme d'autres annotations de type. Les parenthèses sont obligatoires autour des annotations de type dans les modèles. Le code suivant illustre un modèle qui a une annotation de type.
let detect1 x =
match x with
| 1 -> printfn "Found a 1!"
| (var1 : int) -> printfn "%d" var1
detect1 0
detect1 1
Modèle de test de type
Le modèle de test de type est utilisé pour faire correspondre l'entrée à un type. Si le type d'entrée correspond au type spécifié dans le modèle ou est un type dérivé de celui-ci, la correspondance aboutit.
L'exemple suivant illustre le modèle de test de type.
open System.Windows.Forms
let RegisterControl(control:Control) =
match control with
| :? Button as button -> button.Text <- "Registered."
| :? CheckBox as checkbox -> checkbox.Text <- "Registered."
| _ -> ()
Modèle Null
Le modèle Null met en correspondances la valeur Null qui peut apparaître lorsque vous travaillez avec des types qui autorisent une valeur Null. Les modèles Null sont fréquemment utilisés lors de l'interaction avec le code .NET Framework. Par exemple, la valeur de retour d'une API .NET peut être l'entrée d'une expression match. Vous pouvez contrôler le flux de programme de contrôle selon que la valeur de retour est Null ou non, mais aussi en fonction d'autres caractéristiques de la valeur retournée. Vous pouvez utiliser le modèle Null pour empêcher la propagation de valeurs Null dans le reste de votre programme.
L'exemple suivant utilise le modèle Null et le modèle de variable.
let ReadFromFile (reader : System.IO.StreamReader) =
match reader.ReadLine() with
| null -> printfn "\n"; false
| line -> printfn "%s" line; true
let fs = System.IO.File.Open("..\..\Program.fs", System.IO.FileMode.Open)
let sr = new System.IO.StreamReader(fs)
while ReadFromFile(sr) = true do ()
sr.Close()