Informationen
Das angeforderte Thema wird unten angezeigt. Es ist jedoch nicht in dieser Bibliothek vorhanden.

Unterscheidungs-Union (F#)

Unterscheidungs-Unions bieten Unterstützung für Werte, bei denen es sich um einen Fall aus einer Anzahl verschiedener benannter Fälle handeln kann, mit jeweils potenziell unterschiedlichen Werten und Typen. Unterscheidungs-Unions sind für heterogene Daten nützlich: Daten, die besondere Fälle enthalten können, einschließlich gültiger und fehlerhafter Fälle, Daten, die sich im Typ von einer Instanz zur nächsten unterscheiden und als Alternative für kleine Objekthierarchien. Außerdem werden rekursive Unterscheidungs-Unions verwendet, um Baumstrukturdatenstrukturen darzustellen.

type type-name =
   | case-identifier1 [of [ fieldname1 : ] type1 [ * [ fieldname2 : ] type2 ...]
   | case-identifier2 [of [fieldname3 : ]type3 [ * [ fieldname4 : ]type4 ...]
   ...

Unterscheidungs-Unions ähneln Union-Typen in anderen Sprachen, aber es gibt Unterschiede. Wie bei einem Union-Typ in C++ oder einem Variantentyp in Visual Basic werden die im Wert gespeicherten Daten nicht korrigiert. Der Typ kann einer von mehreren unterschiedlichen Optionen sein. Im Gegensatz zu Unions in diesen anderen Sprachen wird jeder der möglichen Optionen ein Fallbezeichner zugewiesen. Die Fallbezeichner sind Namen für die verschiedenen möglichen Typen von Werten, die Objekte dieses Typs aufweisen können. Die Werte sind optional. Wenn keine Werte vorhanden sind, entspricht der Fall einem Enumerationsfall. Wenn Werte vorhanden sind, kann jeder Wert entweder ein einzelner Wert eines angegebenen Typs oder ein Tupel sein, der mehrere Felder gleicher oder unterschiedlicher Typen aggregiert. Ab F# 3.1 können Sie einem einzelnen Feld einen Namen geben, der Name ist jedoch optional, selbst wenn andere Felder im gleichen Fall benannt sind.

Betrachten Sie zum Beispiel die folgende Deklaration eines Formtyps:

type Shape =
    | Rectangle of width : float * length : float
    | Circle of radius : float
    | Prism of width : float * float * height : float

Der vorangehende Code deklariert eine Unterscheidungs-Union-Form, die Werte der folgenden drei Fälle haben kann: Rechteck, Kreis und Prisma. Jeder Fall hat einen anderen Satz von Feldern. Der Rechteckfall hat zwei benannte Felder, beide vom Typ float mit den Namen Breite und Länge. Der Kreisfall hat nur ein benanntes Feld, nämlich Radius. Der Prismafall hat drei Felder, von denen zwei als "Unbenannt" benannte sind und als anonyme Felder bezeichnet werden.

Sie erstellen Objekte, indem Sie Werte für die benannten und anonymen Felder bereitstellen, wie in den folgenden Beispielen dargestellt.

let rect = Rectangle(length = 1.3, width = 10.0)
let circ = Circle (1.0)
let prism = Prism(5., 2.0, height = 3.0)

Dieser Code zeigt, dass Sie entweder die benannten Felder in der Initialisierung verwenden können, oder Sie können die Reihenfolge der Felder in der Deklaration erstellen und nur die Werte für jedes Feld bereitstellen. Der Konstruktoraufruf für rect im vorherigen Code verwendet benannte Felder, der Konstruktoraufruf für circ hingegen die Reihenfolge. Sie können die angeordneten Felder und benannten Felder wie in der Konstruktion von prism kombinieren.

Der option-Typ ist eine einfache Unterscheidungs-Union in der F#-Kernbibliothek. Der option-Typ wird folgendermaßen deklariert.

// The option type is a discriminated union.
type Option<'a> =
    | Some of 'a
    | None

Der vorherige Code gibt an, dass der Option-Typ eine Unterscheidungs-Union mit den beiden Fällen Some und None ist. Der Some-Fall verfügt über einen zugeordneten Wert, der aus einem anonymen Feld besteht, dessen Typ durch den Typparameter 'a dargestellt wird. Der None-Fall verfügt über keinen zugeordneten Wert. Der option-Typ gibt also einen generischen Typ an, der entweder einen Wert eines Typs oder keinen Wert aufweist. Der Option-Typ weist außerdem ein kleingeschriebenes Typalias auf, option, das häufig verwendet wird.

Die Fallbezeichner können für den Unterscheidungs-Union-Typ als Konstruktoren verwendet werden. Der folgende Code wird z. B. verwendet, um Werte des option-Typs zu erstellen.


let myOption1 = Some(10.0)
let myOption2 = Some("string")
let myOption3 = None


Die Fallbezeichner werden auch in Mustervergleichsausdrücken verwendet. In einem Mustervergleichsausdruck werden Bezeichner für die Werte bereitgestellt, die den einzelnen Fällen zugeordnet sind. Im folgenden Code ist x z. B. der Bezeichner, dem der Wert des Some-Falls des option-Typs zugeordnet ist.


let printValue opt =
    match opt with
    | Some x -> printfn "%A" x
    | None -> printfn "No value."


in Mustervergleichsausdrücken können Sie benannte Felder verwenden, um Unterscheidungs-Union-Übereinstimmungen anzugeben. Für den zuvor deklarierten Formtyp können Sie die benannten Felder verwenden, um die Werte von Feldern zu extrahieren, wie der folgende Code zeigt.

let getShapeHeight shape =
    match shape with
    | Rectangle(height = h) -> h
    | Circle(radius = r) -> 2. * r
    | Prism(height = h) -> h

Normalerweise können die Fallbezeichner verwendet werden, ohne sie durch den Namen der Union zu qualifizieren. Soll der Name immer mit dem Namen der Union qualifiziert werden, können Sie das RequireQualifiedAccess-Attribut für die Union-Typdefinition übernehmen.

Verwenden von Unterscheidungs-Unions statt Objekthierarchien

Sie können oft eine Unterscheidungs-Union als einfachere Alternative zu einer kleinen Objekthierarchie verwenden. Die folgende Unterscheidungs-Union könnte z. B. statt einer Shape-Basisklasse verwendet werden, die abgeleitete Typen für Kreis, Quadrat usw. aufweist.


type Shape =
  // The value here is the radius.
| Circle of float
  // The value here is the side length.
| EquilateralTriangle of double
  // The value here is the side length.
| Square of double
  // The values here are the height and width.
| Rectangle of double * double


Statt einer virtuellen Methode zur Berechnung eines Bereich oder eines Umkreises, wie sie in einer objektorientierten Implementierung verwendet werden würde, können Sie mithilfe des Mustervergleichs die entsprechenden Formeln aufteilen, um die Mengen zu berechnen. Im folgenden Beispiel werden andere Formeln verwendet, um den Bereich abhängig von der Form zu berechnen.


let pi = 3.141592654

let area myShape =
    match myShape with
    | Circle radius -> pi * radius * radius
    | EquilateralTriangle s -> (sqrt 3.0) / 4.0 * s * s
    | Square s -> s * s
    | Rectangle (h, w) -> h * w

let radius = 15.0
let myCircle = Circle(radius)
printfn "Area of circle that has radius %f: %f" radius (area myCircle)

let squareSide = 10.0
let mySquare = Square(squareSide)
printfn "Area of square that has side %f: %f" squareSide (area mySquare)

let height, width = 5.0, 10.0
let myRectangle = Rectangle(height, width)
printfn "Area of rectangle that has height %f and width %f is %f" height width (area myRectangle)


Die Ausgabe lautet wie folgt:

Area of circle that has radius 15.000000: 706.858347
Area of square that has side 10.000000: 100.000000
Area of rectangle that has height 5.000000 and width 10.000000 is 50.000000

Verwenden von Unterscheidungs-Unions für Baumdatenstrukturen

Unterscheidungs-Unions können rekursiv sein, d. h., dass die Union selbst im Typ eines Falles oder mehrerer Fälle enthalten sein kann. Rekursive Unterscheidungs-Unions können verwendet werden, um Strukturen zur Gestaltung von Ausdrücken in Programmiersprachen zu erstellen. Im folgenden Code wird eine rekursive Unterscheidungs-Union verwendet, um eine binäre Strukturdatenstruktur zu erstellen. Die Union besteht aus zwei Fällen: Node, der ein Knoten mit einem ganzzahligen Wert und linken und rechten Teilstrukturen ist, und Tip, der die Struktur beendet.


type Tree =
    | Tip
    | Node of int * Tree * Tree

let rec sumTree tree =
    match tree with
    | Tip -> 0
    | Node(value, left, right) ->
        value + sumTree(left) + sumTree(right)
let myTree = Node(0, Node(1, Node(2, Tip, Tip), Node(3, Tip, Tip)), Node(4, Tip, Tip))
let resultSumTree = sumTree myTree


Im vorherigen Code verfügt resultSumTree über den Wert 10. Die folgende Abbildung zeigt die Struktur für myTree an.

Baumstruktur für myTree

Strukturdiagramm für diskriminierte Unions

Unterscheidungs-Unions funktionieren gut, wenn die Knoten in der Struktur heterogen sind. Im folgenden Code stellt der Expression-Typ die abstrakte Syntaxstruktur eines Ausdrucks in einer einfachen Programmiersprache dar, die Addition und Multiplikation von Zahlen und Variablen unterstützt. Einige der Union-Fälle sind nicht rekursiv und stellen entweder Zahlen (Number) oder Variablen (Variable) dar. Andere Fälle sind rekursiv und stellen Operationen (Add und Multiply) dar, wobei die Operanden auch Ausdrücke sind. Die Evaluate-Funktion verwendet einen Vergleichsausdruck, um den Syntaxbaum rekursiv zu verarbeiten.


type Expression = 
    | Number of int
    | Add of Expression * Expression
    | Multiply of Expression * Expression
    | Variable of string

let rec Evaluate (env:Map<string,int>) exp = 
    match exp with
    | Number n -> n
    | Add (x, y) -> Evaluate env x + Evaluate env y
    | Multiply (x, y) -> Evaluate env x * Evaluate env y
    | Variable id    -> env.[id]

let environment = Map.ofList [ "a", 1 ;
                               "b", 2 ;
                               "c", 3 ]

// Create an expression tree that represents
// the expression: a + 2 * b.
let expressionTree1 = Add(Variable "a", Multiply(Number 2, Variable "b"))

// Evaluate the expression a + 2 * b, given the
// table of values for the variables.
let result = Evaluate environment expressionTree1


Wenn dieser Code ausgeführt wird, beträgt der Wert von result 5.

Fanden Sie dies hilfreich?
(1500 verbleibende Zeichen)
Vielen Dank für Ihr Feedback.

Community-Beiträge

Microsoft führt eine Onlineumfrage durch, um Ihre Meinung zur MSDN-Website zu erfahren. Wenn Sie sich zur Teilnahme entscheiden, wird Ihnen die Onlineumfrage angezeigt, sobald Sie die MSDN-Website verlassen.

Möchten Sie an der Umfrage teilnehmen?
Anzeigen:
© 2014 Microsoft