F#
Syndicats discriminés
Recherche…
Nommer des éléments de tuples dans des unions discriminées
Lorsque vous définissez des unions discriminées, vous pouvez nommer des éléments de types de tuple et utiliser ces noms lors de la correspondance de modèle.
type Shape =
| Circle of diameter:int
| Rectangle of width:int * height:int
let shapeIsTenWide = function
| Circle(diameter=10)
| Rectangle(width=10) -> true
| _ -> false
En outre, la dénomination des éléments des unions discriminées améliore la lisibilité du code et l 'interopérabilité avec les noms fournis par C # sera utilisée pour les noms de propriétés et les paramètres des constructeurs. Les noms générés par défaut dans le code interop sont "Item", "Item1", "Item2" ...
Usage de base discriminant des syndicats
Les unions discriminées dans F # permettent de définir des types pouvant contenir un nombre quelconque de types de données différents. Leur fonctionnalité est similaire à celle des unions C ++ ou des variantes VB, mais avec l'avantage supplémentaire d'être sans risque de type.
// define a discriminated union that can hold either a float or a string
type numOrString =
| F of float
| S of string
let str = S "hi" // use the S constructor to create a string
let fl = F 3.5 // use the F constructor to create a float
// you can use pattern matching to deconstruct each type
let whatType x =
match x with
| F f -> printfn "%f is a float" f
| S s -> printfn "%s is a string" s
whatType str // hi is a string
whatType fl // 3.500000 is a float
Union-style unions
Il n'est pas nécessaire d'inclure les informations de type dans les cas d'un syndicat discriminé. En omettant les informations de type, vous pouvez créer une union qui représente simplement un ensemble de choix, similaire à un enum.
// This union can represent any one day of the week but none of
// them are tied to a specific underlying F# type
type DayOfWeek = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
Conversion vers et depuis des chaînes avec Reflection
Parfois, il est nécessaire de convertir une union discriminée à partir d'une chaîne:
module UnionConversion
open Microsoft.FSharp.Reflection
let toString (x: 'a) =
match FSharpValue.GetUnionFields(x, typeof<'a>) with
| case, _ -> case.Name
let fromString<'a> (s : string) =
match FSharpType.GetUnionCases typeof<'a> |> Array.filter (fun case -> case.Name = s) with
| [|case|] -> Some(FSharpValue.MakeUnion(case, [||])) :?> 'a)
| _ -> None
Union discriminée dans un seul cas
Un seul syndicat discriminé est comme n'importe quel autre syndicat discriminé, sauf qu'il n'a qu'un seul cas.
// Define single-case discriminated union type.
type OrderId = OrderId of int
// Construct OrderId type.
let order = OrderId 123
// Deconstruct using pattern matching.
// Parentheses used so compiler doesn't think it is a function definition.
let (OrderId id) = order
Il est utile pour renforcer la sécurité des types et couramment utilisé dans F #, par opposition à C # et Java, où la création de nouveaux types est associée à des frais supplémentaires.
Les deux définitions de type suivantes permettent de déclarer la même union discriminée:
type OrderId = | OrderId of int
type OrderId =
| OrderId of int
Utiliser des syndicats discriminés à cas unique comme enregistrements
Parfois, il est utile de créer des types d'union avec un seul cas pour implémenter des types de type enregistrement:
type Point = Point of float * float
let point1 = Point(0.0, 3.0)
let point2 = Point(-2.5, -4.0)
Celles-ci deviennent très utiles car elles peuvent être décomposées via une correspondance de modèle de la même manière que les arguments tuple peuvent:
let (Point(x1, y1)) = point1
// val x1 : float = 0.0
// val y1 : float = 3.0
let distance (Point(x1,y1)) (Point(x2,y2)) =
pown (x2-x1) 2 + pown (y2-y1) 2 |> sqrt
// val distance : Point -> Point -> float
distance point1 point2
// val it : float = 7.433034374
RequireQualifiedAccess
Avec l'attribut RequireQualifiedAccess
, les cas d'union doivent être appelés MyUnion.MyCase
au lieu de MyCase
. Cela empêche les collisions de noms dans l'espace de noms ou le module englobant:
type [<RequireQualifiedAccess>] Requirements =
None | Single | All
// Uses the DU with qualified access
let noRequirements = Requirements.None
// Here, None still refers to the standard F# option case
let getNothing () = None
// Compiler error unless All has been defined elsewhere
let invalid = All
Si, par exemple, le System
a été ouvert, Single
fait référence à System.Single
. Il n'y a pas de collision avec le cas du syndicat Requirements.Single
.
Unions discriminatoires récursives
Type récursif
Les syndicats discriminés peuvent être récursifs, c'est-à-dire qu'ils peuvent se référer à eux-mêmes dans leur définition. Le principal exemple est un arbre:
type Tree =
| Branch of int * Tree list
| Leaf of int
A titre d'exemple, définissons l'arbre suivant:
1
2 5
3 4
Nous pouvons définir cet arbre en utilisant notre union discriminée récursive comme suit:
let leaf1 = Leaf 3
let leaf2 = Leaf 4
let leaf3 = Leaf 5
let branch1 = Branch (2, [leaf1; leaf2])
let tip = Branch (1, [branch1; leaf3])
Itérer sur l’arbre n’est plus qu’une question de correspondance:
let rec toList tree =
match tree with
| Leaf x -> [x]
| Branch (x, xs) -> x :: (List.collect toList xs)
let treeAsList = toList tip // [1; 2; 3; 4; 5]
Types récursifs mutuellement dépendants
Une manière d'obtenir la récursivité consiste à avoir des types imbriqués dépendants les uns des autres.
// BAD
type Arithmetic = {left: Expression; op:string; right: Expression}
// illegal because until this point, Expression is undefined
type Expression =
| LiteralExpr of obj
| ArithmeticExpr of Arithmetic
La définition d'un type d'enregistrement directement à l'intérieur d'une union discriminée est obsolète:
// BAD
type Expression =
| LiteralExpr of obj
| ArithmeticExpr of {left: Expression; op:string; right: Expression}
// illegal in recent F# versions
Vous pouvez utiliser le mot and
clé and
pour enchaîner les définitions mutuellement dépendantes:
// GOOD
type Arithmetic = {left: Expression; op:string; right: Expression}
and Expression =
| LiteralExpr of obj
| ArithmeticExpr of Arithmetic