Suche…


Benennung von Elementen von Tupeln innerhalb diskriminierter Gewerkschaften

Beim Definieren diskriminierter Vereinigungen können Sie Elemente von Tupeltypen benennen und diese Namen während des Mustervergleichs verwenden.

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

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

Durch die Benennung der Elemente diskriminierter Vereinigungen wird die Lesbarkeit des Codes und die Interoperabilität mit C # verbessert. Die angegebenen Namen werden für die Namen der Eigenschaften und die Parameter der Konstruktoren verwendet. Standardmäßig generierte Namen im Interop-Code sind "Item", "Item1", "Item2" ...

Grundlegende diskriminierte Unionsnutzung

Diskriminierte Vereinigungen in F # bieten eine Möglichkeit, Typen zu definieren, die eine beliebige Anzahl unterschiedlicher Datentypen enthalten können. Ihre Funktionalität ähnelt C ++ - Unions oder VB-Varianten, bietet jedoch den zusätzlichen Vorteil, dass sie typensicher ist.

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

Enum-Style-Vereinigungen

In den Fällen einer diskriminierten Gewerkschaft muss keine Typinformation enthalten sein. Durch das Weglassen von Typinformationen können Sie eine Vereinigung erstellen, die einfach eine Reihe von Auswahlmöglichkeiten darstellt, ähnlich einer Aufzählung.

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

Konvertierung in und aus Strings mit Reflection

Manchmal ist es notwendig, eine diskriminierte Union in eine und aus einer Zeichenfolge zu konvertieren:

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

Einzelfall diskriminierte Gewerkschaft

Eine einzelne diskriminierte Gewerkschaft ist wie jede andere diskriminierte Gewerkschaft, außer dass es nur einen einzigen Fall gibt.

// 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 ist nützlich, um die Typsicherheit zu erzwingen und wird häufig in F # verwendet, im Gegensatz zu C # und Java, wo das Erstellen neuer Typen mehr Aufwand verursacht.

Die folgenden zwei alternativen Typdefinitionen führen dazu, dass dieselbe diskriminierte Einheit für einen Fall deklariert wird:

type OrderId = | OrderId of int

type OrderId =
      | OrderId of int

Verwendung diskriminierter Ein-Fall-Vereinigungen als Datensätze

Manchmal ist es hilfreich, Unionstypen mit nur einem Fall zu erstellen, um datensatzartige Typen zu implementieren:

type Point = Point of float * float

let point1 = Point(0.0, 3.0)

let point2 = Point(-2.5, -4.0)

Diese werden sehr nützlich, da sie über Pattern Matching auf dieselbe Weise zerlegt werden können wie Tupel-Argumente:

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

Mit dem RequireQualifiedAccess Attribut müssen Union-Fälle als MyUnion.MyCase werden und nicht nur als MyCase . Dies verhindert Namenskollisionen im umschließenden Namespace oder Modul.

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

Wenn beispielsweise System geöffnet wurde, bezieht sich Single auf System.Single . Es gibt keine Kollision mit dem Vereinigungsfall Requirements.Single .

Rekursive diskriminierte Gewerkschaften

Rekursiver Typ

Diskriminierte Gewerkschaften können rekursiv sein, dh sie können sich in ihrer Definition auf sich selbst beziehen. Das Paradebeispiel hier ist ein Baum:

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

Als Beispiel definieren wir den folgenden Baum:

    1
  2   5
3   4

Wir können diesen Baum unter Verwendung unserer rekursiven diskriminierten Vereinigung wie folgt definieren:

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

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

Das Iterieren über den Baum ist nur eine Frage des Musterabgleichs:

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]

Wechselseitig abhängige rekursive Typen

Eine Möglichkeit, eine Rekursion zu erreichen, besteht darin, sich gegenseitig abhängige Typen zu verschachteln.

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

Die Definition eines Datensatztyps direkt in einer diskriminierten Union ist veraltet:

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

Sie können die Verwendung and der Kette voneinander abhängigen Definitionen Stichwort:

// 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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow