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


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