Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Serikat yang didiskriminasi memberikan dukungan untuk nilai yang dapat menjadi salah satu dari sejumlah kasus bernama, mungkin masing-masing dengan nilai dan jenis yang berbeda. Serikat yang diskriminasi berguna untuk data heterogen; data yang dapat memiliki kasus khusus, termasuk kasus yang valid dan kesalahan; data yang bervariasi dalam jenis dari satu instans ke instans lainnya; dan sebagai alternatif untuk hierarki objek kecil. Selain itu, penyatuan diskriminasi rekursif digunakan untuk mewakili struktur data pohon.
Sintaksis
[ attributes ]
type [accessibility-modifier] type-name =
| case-identifier1 [of [ fieldname1 : ] type1 [ * [ fieldname2 : ] type2 ...]
| case-identifier2 [of [fieldname3 : ]type3 [ * [ fieldname4 : ]type4 ...]
[ member-list ]
Komentar
Serikat yang diskriminasi mirip dengan jenis gabungan dalam bahasa lain, tetapi ada perbedaan. Seperti tipe union di C++ atau tipe varian di Visual Basic, data yang disimpan dalam nilai tidak tetap; dapat menjadi salah satu dari beberapa opsi yang berbeda. Namun, tidak seperti penggabungan dalam bahasa-bahasa lain ini, masing-masing opsi yang mungkin diberikan identifikasi kasus . Pengidentifikasi kasus adalah nama untuk berbagai jenis nilai yang mungkin dari objek jenis ini; nilai bersifat opsional. Jika nilai tidak ada, kasus ini setara dengan kasus enumerasi. Jika nilai ada, setiap nilai dapat berupa nilai tunggal dari jenis tertentu, atau tuple yang menggabungkan beberapa bidang dengan jenis yang sama atau berbeda. Anda dapat memberi nama bidang individual, tetapi namanya opsional, bahkan jika bidang lain dalam kasus yang sama diberi nama.
Aksesibilitas untuk serikat yang didiskriminasi menggunakan default public
.
Misalnya, pertimbangkan deklarasi berikut untuk tipe Shape.
type Shape =
| Rectangle of width : float * length : float
| Circle of radius : float
| Prism of width : float * float * height : float
Kode sebelumnya mendeklarasikan Bentuk gabungan yang didiskriminasi, yang dapat memiliki nilai dari salah satu dari tiga kasus: Persegi Panjang, Lingkaran, dan Prisma. Setiap kasus memiliki sekumpulan bidang yang berbeda. Kasus Persegi Panjang memiliki dua bidang bernama, keduanya dari jenis float
, yang memiliki nama lebar dan panjang. Kasus Lingkaran hanya memiliki satu bidang bernama, radius. Kasus Prism memiliki tiga bidang, dua di antaranya (lebar dan tinggi) diberi nama bidang. Bidang yang tidak disebutkan namanya disebut sebagai bidang anonim.
Anda membuat objek dengan menyediakan nilai untuk bidang bernama dan anonim sesuai dengan contoh berikut.
let rect = Rectangle(length = 1.3, width = 10.0)
let circ = Circle (1.0)
let prism = Prism(5., 2.0, height = 3.0)
Kode ini menunjukkan bahwa Anda dapat menggunakan bidang bernama dalam inisialisasi, atau Anda dapat mengandalkan pengurutan bidang dalam deklarasi dan hanya menyediakan nilai untuk setiap bidang secara bergantian. Panggilan konstruktor untuk rect
dalam kode sebelumnya menggunakan bidang bernama, tetapi panggilan konstruktor untuk circ
menggunakan pengurutan. Anda dapat mencampur bidang yang diurutkan dan bidang bernama, seperti pada konstruksi prism
.
Jenis option
adalah penyatuan sederhana yang didiskriminasi di pustaka inti F#. Jenis option
dinyatakan sebagai berikut.
// The option type is a discriminated union.
type Option<'a> =
| Some of 'a
| None
Kode sebelumnya menentukan bahwa tipe Option
adalah union diskriminatif yang memiliki dua kasus, Some
dan None
. Kasus Some
memiliki nilai terkait yang terdiri dari satu bidang anonim yang jenisnya diwakili oleh parameter jenis 'a
. Kasus None
tidak memiliki nilai terkait. Dengan demikian, jenis option
menentukan jenis generik yang bisa memiliki nilai dari suatu tipe atau tidak memiliki nilai sama sekali. Jenis Option
juga memiliki alias jenis huruf kecil, option
, yang lebih umum digunakan.
Pengidentifikasi kasus dapat digunakan sebagai konstruktor untuk tipe union diskriminasi. Misalnya, kode berikut digunakan untuk membuat nilai jenis option
.
let myOption1 = Some(10.0)
let myOption2 = Some("string")
let myOption3 = None
Pengidentifikasi kasus juga digunakan dalam ekspresi pencocokan pola. Dalam ekspresi pencocokan pola, pengidentifikasi disediakan untuk nilai yang terkait dengan kasus individual. Misalnya, dalam kode berikut, x
adalah identifier yang diberikan nilai terkait dengan kasus Some
dari jenis option
.
let printValue opt =
match opt with
| Some x -> printfn "%A" x
| None -> printfn "No value."
Dalam ekspresi pencocokan pola, Anda dapat menggunakan bidang bernama untuk menentukan kecocokan gabungan yang diskriminasi. Untuk Tipe bentuk yang dideklarasikan sebelumnya, Anda bisa menggunakan bidang bernama seperti yang ditunjukkan kode berikut untuk mengekstrak nilai bidang.
let getShapeWidth shape =
match shape with
| Rectangle(width = w) -> w
| Circle(radius = r) -> 2. * r
| Prism(width = w) -> w
Biasanya, pengidentifikasi kasus dapat digunakan tanpa mengaitkannya dengan nama serikat. Jika Anda ingin nama selalu dikualifikasikan dengan nama union, Anda dapat menerapkan atribut RequireQualifiedAccess pada definisi tipe union.
Membongkar Serikat yang Dibedakan
Dalam F#, Discriminated Unions sering digunakan dalam pemodelan domain untuk membungkus tipe tunggal. Sangat mudah untuk mengekstrak nilai yang mendasar melalui pencocokan pola juga. Anda tidak perlu menggunakan ekspresi kecocokan untuk satu kasus:
let ([UnionCaseIdentifier] [values]) = [UnionValue]
Contoh berikut menunjukkan hal ini:
type ShaderProgram = | ShaderProgram of id:int
let someFunctionUsingShaderProgram shaderProgram =
let (ShaderProgram id) = shaderProgram
// Use the unwrapped value
...
Pencocokan pola juga diizinkan langsung dalam parameter fungsi, sehingga Anda dapat mengurai satu kasus di sana.
let someFunctionUsingShaderProgram (ShaderProgram id) =
// Use the unwrapped value
...
Serikat Diskriminatif Struct
Anda juga dapat mewakili Discriminated Unions sebagai struktur. Ini dilakukan dengan atribut [<Struct>]
.
[<Struct>]
type SingleCase = Case of string
[<Struct>]
type Multicase =
| Case1 of string
| Case2 of int
| Case3 of double
Karena ini adalah tipe nilai dan bukan tipe referensi, ada pertimbangan tambahan dibandingkan dengan union diskriminatif referensi.
- Mereka disalin sebagai tipe nilai dan memiliki semantik tipe nilai.
- Anda tidak dapat menggunakan definisi tipe rekursif dengan union terdiskriminasi yang memiliki beberapa kasus struct.
Sebelum F# 9, ada persyaratan untuk setiap kasus untuk menentukan nama kasus unik (dalam serikat). Dimulai dengan F# 9, batasan dicabut.
Menggunakan Union Diskriminatif Alih-alih Hierarki Objek
Anda sering dapat menggunakan serikat yang didiskriminasi sebagai alternatif yang lebih sederhana untuk hierarki objek kecil. Misalnya, penyatuan diskriminasi berikut dapat digunakan alih-alih kelas dasar Shape
yang memiliki jenis turunan untuk lingkaran, persegi, dan sebagainya.
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
Alih-alih metode virtual untuk menghitung area atau perimeter, seperti yang akan Anda gunakan dalam implementasi berorientasi objek, Anda dapat menggunakan pencocokan pola untuk mencabangkan ke rumus yang sesuai untuk menghitung jumlah ini. Dalam contoh berikut, rumus yang berbeda digunakan untuk menghitung area, tergantung pada bentuknya.
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)
Outputnya adalah sebagai berikut:
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
Menggunakan Serikat Yang Diskriminasi untuk Struktur Data Pohon
Serikat yang didiskriminasi dapat menjadi rekursif, yang berarti bahwa serikat itu sendiri dapat dimasukkan dalam jenis satu atau beberapa kasus. Penyatuan diskriminasi rekursif dapat digunakan untuk membuat struktur pohon, yang digunakan untuk memodelkan ekspresi dalam bahasa pemrograman. Dalam kode berikut, penyatuan diskriminasi rekursif digunakan untuk membuat struktur data pohon biner. Penyatuan terdiri dari dua kasus, Node
, yang merupakan simpul dengan nilai bilangan bulat dan subtrees kiri dan kanan, dan Tip
, yang mengakhiri pohon.
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
Dalam kode sebelumnya, resultSumTree
memiliki nilai 10. Ilustrasi berikut menunjukkan struktur pohon untuk myTree
.
Union diskrit berfungsi dengan baik jika node pada pohon heterogen. Dalam kode berikut, jenis Expression
mewakili pohon sintaks abstrak ekspresi dalam bahasa pemrograman sederhana yang mendukung penambahan dan perkalian angka dan variabel. Beberapa kasus gabungan tidak rekursif dan mewakili angka (Number
) atau variabel (Variable
). Kasus lain bersifat rekursif, dan mewakili operasi (Add
dan Multiply
), di mana operand juga merupakan ekspresi. Fungsi Evaluate
menggunakan ekspresi kecocokan untuk memproses pohon sintaks secara rekursif.
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
Ketika kode ini dijalankan, nilai result
adalah 5.
Union Terdiskriminasi Rekursif Saling Berkaitan
Serikat buruh yang diskriminasi dalam F# dapat saling rekursif, yang berarti bahwa beberapa jenis serikat dapat saling mereferensikan satu sama lain secara rekursif. Ini berguna saat memodelkan struktur hierarkis atau interkoneksi. Untuk menentukan serikat yang didiskriminasi bersama secara rekursif, gunakan kata kunci and
.
Misalnya, pertimbangkan representasi pohon sintaks abstrak (AST) di mana ekspresi dapat menyertakan pernyataan, dan pernyataan dapat berisi ekspresi:
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
Anggota
Dimungkinkan untuk menentukan anggota pada serikat yang dibedakan. Contoh berikut menunjukkan cara menentukan properti dan menerapkan antarmuka:
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}"
.Is*
properti pada kasus
Sejak F# 9, serikat yang didiskriminasi mengekspos properti .Is*
yang dihasilkan secara otomatis untuk setiap kasus, memungkinkan Anda untuk memeriksa apakah nilainya adalah kasus tertentu.
Ini adalah bagaimana hal ini dapat digunakan:
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
Atribut umum
Atribut berikut biasanya terlihat dalam serikat yang diskriminasi:
[<RequireQualifiedAccess>]
[<NoEquality>]
[<NoComparison>]
[<Struct>]