Aracılığıyla paylaş


Ayırt Edilmiş Birleşimler

Ayrımcı birleşimler, büyük olasılıkla her biri farklı değerlere ve türlere sahip bir dizi adlandırılmış durumdan biri olabilecek değerler için destek sağlar. Ayrımcı birleşimler heterojen veriler için yararlıdır; geçerli ve hata durumları dahil olmak üzere özel durumlara sahip olabilecek veriler; bir örnekten diğerine türe göre değişen veriler; ve küçük nesne hiyerarşileri için bir alternatif olarak. Ayrıca, özyinelemeli ayrımcı birleşimler ağaç veri yapılarını temsil etmek için kullanılır.

Sözdizimi

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

    [ member-list ]

İlk kanal karakteri (|) isteğe bağlıdır. Tek durumlu ayrımcı birleşimler için bunu atlayabilirsiniz:

type SingleCase = Case of string

Açıklamalar

Ayrımcı birleşimler diğer dillerdeki birleşim türlerine benzer, ancak farklılıklar vardır. C++ içindeki birleşim türünde veya Visual Basic'te değişken türünde olduğu gibi, değerde depolanan veriler sabit değildir; çeşitli seçeneklerden biri olabilir. Ancak, bu diğer dillerdeki birleşimlerden farklı olarak, olası seçeneklerin her birine birdurum tanımlayıcısı verilir. İşlem tanımlayıcıları, bu türdeki nesnelerin alabileceği çeşitli olası değer türlerinin adlarıdır; değerler isteğe bağlıdır. Değerler yoksa, durum bir numaralandırma durumu ile eşdeğerdir. Eğer değerler varsa, her değer belirtilen türde tek bir değer veya aynı ya da farklı türlerde birden çok alanı bir araya getiren bir demet olabilir. Tek bir alana bir ad verebilirsiniz, ancak aynı durumdaki diğer alanlar adlandırılmış olsa bile ad isteğe bağlıdır.

Ayrımcılığa uğramış sendikalar için varsayılan erişilebilirlik publicolarak ayarlanır.

Örneğin, bir Şekil türünün aşağıdaki bildirimini göz önünde bulundurun.

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

Yukarıdaki kod, üç durumdan herhangi birinin değerine sahip olabilecek ayrımcı birleşim Şeklini bildirir: Dikdörtgen, Daire ve Prizma. Her vakanın farklı bir alan seti vardır. Dikdörtgen durumu, her ikisi de floattipi olan genişlik ve uzunluk isimlerine sahip iki adlandırılmış alana sahiptir. Circle olayının radius adlı tek bir alanı vardır. Prism örneğinde ikisi (genişlik ve yükseklik) olarak adlandırılan üç alan vardır. Adsız alanlar anonim alanlar olarak adlandırılır.

Adlandırılmış ve anonim alanlar için aşağıdaki örneklere göre değerler sağlayarak nesneleri oluşturursunuz.

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

Bu kod, başlatmada adlandırılmış alanları kullanabileceğinizi veya bildirimdeki alanların sıralamasına güvenebileceğinizi ve her alanın değerlerini sırayla sağlayabileceğinizi gösterir. Önceki koddaki rect oluşturucu çağrısı adlandırılmış alanları kullanır, ancak circ oluşturucu çağrısı sıralamayı kullanır. prismyapısında olduğu gibi sıralı alanları ve adlandırılmış alanları karıştırabilirsiniz.

option türü, F# çekirdek kitaplığındaki basit bir ayrımcı birleşimdir. option türü aşağıdaki gibi bildirilir.

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

Önceki kod, Option türünün Some ve Noneolmak üzere iki örneği olan ayrımcı bir birleşim olduğunu belirtir. Some durumu, türü 'atür parametresiyle temsil edilen bir anonim alandan oluşan ilişkili bir değere sahiptir. None durumunun ilgili bir değeri yok. Bu nedenle option türü, bir tür değerine sahip olan veya değer içermeyen genel bir tür belirtir. Option türü, daha yaygın kullanılan küçük harfle yazılan optionadlı bir takma ada da sahiptir.

Durum tanımlayıcıları, ayırt edici birleşim türü için oluşturucu olarak kullanılabilir. Örneğin, aşağıdaki kod option türündeki değerleri oluşturmak için kullanılır.

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

Durum tanımlayıcıları, desen eşleştirme ifadelerinde de kullanılır. Desen eşleme ifadesinde, birbirine ait durumlarla ilişkili değerler için tanımlayıcılar sağlanır. Örneğin, aşağıdaki kodda x, Some türünün option durumu ile ilişkili değerin atandığı tanımlayıcıdır.

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

Desen eşleştirme ifadelerinde, ayrımcı birleşim eşleşmelerini belirtmek için adlandırılmış alanları kullanabilirsiniz. Daha önce bildirilen Şekil türü için, aşağıdaki kodda gösterildiği gibi adlandırılmış alanları kullanarak alanların değerlerini ayıklayabilirsiniz.

let getShapeWidth shape =
    match shape with
    | Rectangle(width = w) -> w
    | Circle(radius = r) -> 2. * r
    | Prism(width = w) -> w

Normalde, vaka tanımlayıcıları birlik adıyla nitelemeden kullanılabilir. Adın her zaman birleşim adıyla nitelenmiş olmasını istiyorsanız, RequireQualifiedAccess özniteliğini birleşim türü tanımına uygulayabilirsiniz.

Ayrımcı Birleşimleri Açma

F# 'de Ayrımcı Birleşimler genellikle etki alanı modellemesinde tek bir türü sarmalamada kullanılır. Desen eşleştirmesi aracılığıyla temel alınan değeri ayıklamak da kolaydır. Tek bir olay için eşleşme ifadesi kullanmanız gerekmez:

let ([UnionCaseIdentifier] [values]) = [UnionValue]

Aşağıdaki örnek bunu gösterir:

type ShaderProgram = | ShaderProgram of id:int

let someFunctionUsingShaderProgram shaderProgram =
    let (ShaderProgram id) = shaderProgram
    // Use the unwrapped value
    ...

Desen eşleştirmesi doğrudan işlev parametrelerinde de kullanılabilir, böylece burada tek bir durumu açabilirsiniz.

let someFunctionUsingShaderProgram (ShaderProgram id) =
    // Use the unwrapped value
    ...

Ayrımcı Yapı Birleşimleri

Ayrıca, Ayrımcı Birleşimleri yapılar olarak da temsil edebilirsiniz. Bu işlem [<Struct>] özniteliğiyle yapılır.

[<Struct>]
type SingleCase = Case of string

[<Struct>]
type Multicase =
    | Case1 of string
    | Case2 of int
    | Case3 of double

Bunlar değer türleri ve bağlamayla ilişkilendirilen türler olmadığı için, referans ayırt edici birleşimlerle kıyaslandığında dikkat edilmesi gereken ek noktalar vardır.

  1. Bunlar değer türü olarak kopyalanır ve değer türü semantiğine sahiptir.
  2. Özyinelemeli tür tanımını çok durumlu yapı ayrımsal birleşimle kullanamazsınız.

F# 9'dan önce, her durum için benzersiz bir durum adı belirtme gereksinimi vardı (birleşim içinde). F# 9'dan itibaren sınırlama kaldırılır.

Nesne Hiyerarşileri Yerine Ayrımcı Birleşimler Kullanma

Küçük bir nesne hiyerarşisine daha basit bir alternatif olarak genellikle ayrımcı bir birleşim kullanabilirsiniz. Örneğin, daire, kare vb. türleri türeten bir Shape temel sınıfı yerine aşağıdaki ayırt edici birlik kullanılabilir.

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

Nesne odaklı bir uygulamada kullandığınız gibi, bir alanı veya çevreyi hesaplamak için sanal bir yöntem yerine, bu miktarları hesaplamak için uygun formüllerle dallanan desen eşleştirmeyi kullanabilirsiniz. Aşağıdaki örnekte, şekle bağlı olarak alanı hesaplamak için farklı formüller kullanılmıştır.

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)

Çıkış aşağıdaki gibidir:

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

Ağaç Veri Yapıları Üzerinde Ayrımcı Birleşimler Kullanma

Ayrımcı birleşimler özyinelemeli olabilir, yani birleşimin kendisi bir veya daha fazla durum türüne dahil edilebilir. Özyinelemeli ayrımcı birleşimler, programlama dillerindeki ifadeleri modellemek için kullanılan ağaç yapıları oluşturmak için kullanılabilir. Aşağıdaki kodda, ikili ağaç veri yapısı oluşturmak için özyinelemeli ayrımcı bir birleşim kullanılır. Birleşim, sol ve sağ alt ağaçlara sahip bir tamsayı değeri içeren bir düğüm olan Nodeve ağacı sonlandıran Tipolmak üzere iki durumdan oluşur.

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

Önceki kodda resultSumTree değeri 10'dır. Aşağıdaki çizimde myTreeiçin ağaç yapısı gösterilmektedir.

myTree için ağaç yapısını gösteren Diyagramı.

Ağaçtaki düğümler heterojen olduğunda, ayrımcı birliktelikler iyi çalışır. Aşağıdaki kodda, Expression türü, sayıların ve değişkenlerin eklenmesini ve çarpımını destekleyen basit bir programlama dilinde ifadenin soyut söz dizimi ağacını temsil eder. Birleşim durumlarından bazıları özyinelemeli değildir ve sayıları (Number) veya değişkenleri (Variable) temsil eder. Özyinelemeli olan diğer durumlar, işlenenlerin de birer ifade olduğu işlemler (Add ve Multiply) olarak temsil edilir. Evaluate işlevi, söz dizimi ağacını yinelemeli olarak işlemek için bir eşleştirme ifadesi kullanır.

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 [ "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

Bu kod yürütüldüğünde, result değeri 5'tir.

Karşılıklı Özyinelemeli Ayrımcı Birleşimler

F# dilindeki ayrımcı birleşimler karşılıklı özyinelemeli olabilir, yani birden çok birleşim türü özyinelemeli bir şekilde birbirine başvurabilir. Bu, hiyerarşik veya birbirine bağlı yapıları modellemek için kullanışlıdır. Karşılıklı özyinelemeli ayrımcı birleşimleri tanımlamak için and anahtar sözcüğünü kullanın.

Örneğin, ifadelerin deyimleri içerebildiği ve deyimlerin ifade içerebildiği soyut söz dizimi ağacı (AST) gösterimini göz önünde bulundurun:

type Expression =
    | Literal of int
    | Variable of string
    | Operation of string * Expression * Expression
and Statement =
    | Assign of string * Expression
    | Sequence of Statement list
    | IfElse of Expression * Statement * Statement

Üyeler

Ayrımcı birleşimlerde üye tanımlamak mümkündür. Aşağıdaki örnekte bir özelliği tanımlama ve arabirim uygulama gösterilmektedir:

open System

type IPrintable =
    abstract Print: unit -> unit

type Shape =
    | Circle of float
    | EquilateralTriangle of float
    | Square of float
    | Rectangle of float * float

    member this.Area =
        match this with
        | Circle r -> Math.PI * (r ** 2.0)
        | EquilateralTriangle s -> s * s * sqrt 3.0 / 4.0
        | Square s -> s * s
        | Rectangle(l, w) -> l * w

    interface IPrintable with
        member this.Print () =
            match this with
            | Circle r -> printfn $"Circle with radius %f{r}"
            | EquilateralTriangle s -> printfn $"Equilateral Triangle of side %f{s}"
            | Square s -> printfn $"Square with side %f{s}"
            | Rectangle(l, w) -> printfn $"Rectangle with length %f{l} and width %f{w}"

Vakalarda .Is* özellikleri

F# 9'dan itibaren, ayrık birlikler her bir vaka için otomatik olarak oluşturulan .Is* özelliklerini kullanıma sunar ve bir değerin belirli bir vakaya ait olup olmadığını denetlemenize olanak tanır.

Bu şekilde kullanılabilir:

type Contact =
    | Email of address: string
    | Phone of countryCode: int * number: string

type Person = { name: string; contact: Contact }

let canSendEmailTo person =
    person.contact.IsEmail      // .IsEmail is auto-generated

Ortak öznitelikler

Aşağıdaki öznitelikler yaygın olarak ayrımcı birleşimlerde görülür:

  • [<RequireQualifiedAccess>]
  • [<NoEquality>]
  • [<NoComparison>]
  • [<Struct>]

Ayrıca bkz.