Ricerca…


Denominazione di elementi di tuple all'interno di sindacati discriminati

Quando si definiscono i sindacati discriminati, è possibile assegnare un nome agli elementi dei tipi di tupla e utilizzare questi nomi durante la corrispondenza dei modelli.

type Shape = 
    | Circle of diameter:int
    | Rectangle of width:int * height:int

let shapeIsTenWide = function
    | Circle(diameter=10) 
    | Rectangle(width=10) -> true
    | _ -> false

Inoltre, la denominazione degli elementi di unioni discriminate migliora la leggibilità del codice e l'interoperabilità con C # - i nomi forniti verranno utilizzati per i nomi delle proprietà e i parametri dei costruttori. I nomi generati predefiniti nel codice di interoperabilità sono "Elemento", "Elemento1", "Elemento2" ...

Utilizzo discriminatorio di base dell'Unione

I sindacati discriminati in F # offrono un modo per definire tipi che possono contenere un numero qualsiasi di tipi di dati diversi. La loro funzionalità è simile alle unioni C ++ o alle varianti VB, ma con l'ulteriore vantaggio di essere sicuro.

// 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

Sindacati in stile Enum

Le informazioni di tipo non devono essere incluse nei casi di unione discriminata. Omettendo le informazioni sul tipo è possibile creare un unione che rappresenta semplicemente un insieme di scelte, simile a 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

Conversione da e verso le stringhe con Reflection

A volte è necessario convertire un'unione discriminata in e da una stringa:

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

Unione discriminata caso singolo

Un'unione discriminata in un singolo caso è come qualsiasi altra unione discriminata, tranne che ha un solo caso.

// 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

È utile per rafforzare la sicurezza del tipo e comunemente usato in F # rispetto a C # e Java, dove la creazione di nuovi tipi comporta un ulteriore sovraccarico.

Le seguenti due definizioni di tipi alternativi provocano la stessa unione discriminata a caso singolo:

type OrderId = | OrderId of int

type OrderId =
      | OrderId of int

Utilizzo di unioni discriminate a caso singolo come record

A volte è utile creare tipi di unione con un solo caso per implementare tipi di record:

type Point = Point of float * float

let point1 = Point(0.0, 3.0)

let point2 = Point(-2.5, -4.0)

Questi diventano molto utili perché possono essere scomposti tramite la corrispondenza dei pattern nello stesso modo in cui gli argomenti di tuple possono:

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

Con l'attributo RequireQualifiedAccess , i casi di unione devono essere indicati come MyUnion.MyCase anziché solo MyCase . Ciò impedisce le collisioni di nomi nello spazio dei nomi o nel modulo allegato:

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

Se, ad esempio, System è stato aperto, Single fa riferimento a System.Single . Non vi è alcuna collisione con il caso del sindacato Requirements.Single .

Sindacati discriminati ricorsivi

Tipo ricorsivo

I sindacati discriminati possono essere ricorsivi, cioè possono riferirsi a se stessi nella loro definizione. L'esempio principale qui è un albero:

type Tree =
    | Branch of int * Tree list
    | Leaf of int

Ad esempio, definiamo il seguente albero:

    1
  2   5
3   4

Possiamo definire questo albero usando la nostra unione discriminata ricorsiva come segue:

let leaf1 = Leaf 3
let leaf2 = Leaf 4
let leaf3 = Leaf 5

let branch1 = Branch (2, [leaf1; leaf2])
let tip = Branch (1, [branch1; leaf3])

L'iterazione sull'albero è quindi solo una questione di corrispondenza del modello:

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]

Tipi ricorsivi mutuamente dipendenti

Un modo per ottenere la ricorsione consiste nell'annidare tipi mutualmente dipendenti.

// 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 definizione di un tipo di record direttamente all'interno di un'unione discriminata è deprecata:

// BAD
type Expression = 
| LiteralExpr of obj
| ArithmeticExpr of {left: Expression; op:string; right: Expression}
// illegal in recent F# versions

È possibile utilizzare la parola chiave and per catene le definizioni reciprocamente dipendenti:

// 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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow