Buscar..


Nombrando elementos de tuplas dentro de uniones discriminadas

Al definir uniones discriminadas, puede nombrar elementos de tipos de tuplas y usar estos nombres durante la comparación de patrones.

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

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

Además, nombrar los elementos de uniones discriminadas mejora la legibilidad del código y la interoperabilidad con C #; los nombres proporcionados se utilizarán para los nombres de las propiedades y los parámetros de los constructores. Los nombres generados predeterminados en el código de interoperabilidad son "Elemento", "Elemento1", "Elemento2" ...

Uso Básico Discriminado de la Unión

Las uniones discriminadas en F # ofrecen una forma de definir tipos que pueden contener cualquier número de tipos de datos diferentes. Su funcionalidad es similar a las uniones C ++ o variantes de VB, pero con el beneficio adicional de ser de tipo seguro.

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

Uniones al estilo Enum

La información de tipo no necesita ser incluida en los casos de una unión discriminada. Al omitir la información de tipo, puede crear una unión que simplemente representa un conjunto de opciones, similar a una enumeración.

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

Convertir hacia y desde cuerdas con Reflexión

A veces es necesario convertir una Unión Discriminada hacia y desde una cadena:

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

Unión individual discriminada caso

Una unión discriminada de un solo caso es como cualquier otra unión discriminada, excepto que solo tiene un 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

Es útil para hacer cumplir la seguridad de tipos y se usa comúnmente en F # en lugar de C # y Java, donde la creación de nuevos tipos conlleva más sobrecarga.

Las siguientes dos definiciones de tipos alternativos dan como resultado que se declare la misma unión discriminada de un solo caso:

type OrderId = | OrderId of int

type OrderId =
      | OrderId of int

Uso de uniones discriminadas de un solo caso como registros

A veces es útil crear tipos de unión con un solo caso para implementar tipos similares a registros:

type Point = Point of float * float

let point1 = Point(0.0, 3.0)

let point2 = Point(-2.5, -4.0)

Se vuelven muy útiles porque pueden descomponerse mediante la coincidencia de patrones de la misma manera que los argumentos de la tupla pueden:

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 el atributo RequireQualifiedAccess , los casos de unión deben denominarse MyUnion.MyCase lugar de solo MyCase . Esto evita las colisiones de nombres en el espacio de nombres o el módulo adjunto:

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, por ejemplo, el System se ha abierto, Single refiere a System.Single . No hay colisión con los Requirements.Single caso del sindicato. Solo.

Uniones recursivas discriminadas

Tipo recursivo

Las uniones discriminadas pueden ser recursivas, es decir, pueden referirse a sí mismas en su definición. El primer ejemplo aquí es un árbol:

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

Como ejemplo, definamos el siguiente árbol:

    1
  2   5
3   4

Podemos definir este árbol usando nuestra unión recursiva discriminada de la siguiente manera:

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

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

Iterar sobre el árbol es solo una cuestión de coincidencia de patrones:

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]

Tipos recursivos mutuamente dependientes

Una forma de lograr la recursión es tener tipos anidados mutuamente dependientes.

// 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 definición de un tipo de registro directamente dentro de una unión discriminada está en desuso:

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

Puede usar la palabra clave and para encadenar definiciones mutuamente dependientes:

// 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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow