차별화된 유니온
차별된 공용 구조체는 여러 개의 명명된 사례 중 하나일 수 있는 값(각각 다른 값 및 형식)을 지원합니다. 식별된 유니온은 이질적인 데이터에 유용합니다. 유효한 사례와 오류 사례를 포함한 특수한 사례를 처리할 수 있으며, 인스턴스마다 형식이 달라질 수 있는 데이터를 다루는 데 적합합니다. 또한, 작은 객체 계층 구조의 대안으로 사용할 수도 있습니다. 또한 재귀적으로 구분된 유니언은 트리 데이터 구조를 나타내는 데 사용됩니다.
통사론
[ attributes ]
type [accessibility-modifier] type-name =
| case-identifier1 [of [ fieldname1 : ] type1 [ * [ fieldname2 : ] type2 ...]
| case-identifier2 [of [fieldname3 : ]type3 [ * [ fieldname4 : ]type4 ...]
[ member-list ]
발언
차별된 공용 구조체는 다른 언어의 공용 구조체 형식과 유사하지만 차이점이 있습니다. C++의 공용 구조체 형식 또는 Visual Basic의 변형 형식과 마찬가지로 값에 저장된 데이터는 고정되지 않습니다. 여러 가지 고유한 옵션 중 하나일 수 있습니다. 그러나 이러한 다른 언어에서의 유니언과 달리, 가능한 각 옵션에는 케이스 식별자이 제공됩니다. 케이스 식별자는 이 형식의 개체가 가질 수 있는 다양한 가능한 값 유형에 대한 이름입니다. 값은 선택 사항입니다. 값이 없으면 사례는 열거형 사례와 같습니다. 값이 있는 경우 각 값은 지정된 형식의 단일 값이거나 동일하거나 다른 형식의 여러 필드를 집계하는 튜플일 수 있습니다. 개별 필드에 이름을 지정할 수 있지만 동일한 경우의 다른 필드 이름이 지정되더라도 이름은 선택 사항입니다.
차별된 유니온의 접근성은 기본값으로 public
로 설정됩니다.
예를 들어 도형 타입의 다음 선언을 고려하십시오.
type Shape =
| Rectangle of width : float * length : float
| Circle of radius : float
| Prism of width : float * float * height : float
앞의 코드는 구분된 공용 구조체 셰이프를 선언합니다. 이 셰이프는 사각형, 원 및 프리즘의 세 가지 사례 중 하나 이상의 값을 가질 수 있습니다. 각 사례에는 서로 다른 필드 집합이 있습니다. 사각형 케이스에는 float
형식의 너비와 길이라는 이름을 가진 두 개의 필드가 있습니다. 원 케이스에는 명명된 필드 반경이 하나뿐입니다. Prism 사례에는 세 개의 필드가 있으며, 그 중 2개(너비 및 높이)는 명명된 필드입니다. 명명되지 않은 필드를 익명 필드라고 합니다.
다음 예제에 따라 명명된 필드와 익명 필드에 대한 값을 제공하여 개체를 생성합니다.
let rect = Rectangle(length = 1.3, width = 10.0)
let circ = Circle (1.0)
let prism = Prism(5., 2.0, height = 3.0)
이 코드는 초기화에서 명명된 필드를 사용하거나 선언에서 필드의 순서에 의존하여 각 필드에 대한 값을 차례로 제공할 수 있음을 보여 있습니다. 이전 코드에서 rect
대한 생성자 호출은 명명된 필드를 사용하지만 circ
대한 생성자 호출은 순서 지정을 사용합니다.
prism
생성과 같이 순서가 지정된 필드와 명명된 필드를 혼합할 수 있습니다.
option
형식은 F# 코어 라이브러리에서 단순한 판별된 공용체입니다.
option
형식은 다음과 같이 선언됩니다.
// The option type is a discriminated union.
type Option<'a> =
| Some of 'a
| None
이전 코드는 Option
유형이 두 가지 선택지, 즉 Some
및 None
를 가진 구분 유니온임을 지정합니다.
Some
사례에는 형식 매개 변수 'a
형식으로 표현되는 익명 필드 하나로 구성된 연결된 값이 있습니다.
None
사례에는 연결된 값이 없습니다. 따라서 option
형식은 일부 형식의 값이 있거나 값이 없는 제네릭 형식을 지정합니다.
Option
형식에는 더 일반적으로 사용되는 소문자 형식 별칭(option
)도 있습니다.
식별자는 차별 합집합 형식의 생성자로 사용할 수 있습니다. 예를 들어 다음 코드는 option
형식의 값을 만드는 데 사용됩니다.
let myOption1 = Some(10.0)
let myOption2 = Some("string")
let myOption3 = None
사례 식별자는 패턴 매칭 표현식에도 사용됩니다. 패턴 일치 식에서 개별 사례와 연결된 값에 대한 식별자가 제공됩니다. 예를 들어 다음 코드에서 x
는 option
유형의 Some
사례와 연관된 값을 지정받은 식별자입니다.
let printValue opt =
match opt with
| Some x -> printfn "%A" x
| None -> printfn "No value."
패턴 일치 식에서 명명된 필드를 사용하여 구분된 공용 구조체 일치를 지정할 수 있습니다. 이전에 선언된 셰이프 형식의 경우 다음 코드와 같이 명명된 필드를 사용하여 필드 값을 추출할 수 있습니다.
let getShapeWidth shape =
match shape with
| Rectangle(width = w) -> w
| Circle(radius = r) -> 2. * r
| Prism(width = w) -> w
일반적으로 사례 식별자는 유니언(Union)의 이름으로 한정하지 않고 사용할 수 있습니다. 이름을 항상 유니온의 이름으로 식별하도록 하려면 RequireQualifiedAccess 특성을 유니온 형식 정의에 적용할 수 있습니다.
구분합집합 풀기
F#의 판별 공용체는 단일 타입을 래핑하기 위해 도메인 모델링에서 자주 사용됩니다. 패턴 일치를 통해 기본 값을 쉽게 추출할 수 있습니다. 단일 사례에 대해 일치 식을 사용할 필요가 없습니다.
let ([UnionCaseIdentifier] [values]) = [UnionValue]
다음 예제에서는 이를 보여 줍니다.
type ShaderProgram = | ShaderProgram of id:int
let someFunctionUsingShaderProgram shaderProgram =
let (ShaderProgram id) = shaderProgram
// Use the unwrapped value
...
패턴 일치는 함수 매개 변수에서도 직접 허용되므로 다음과 같이 단일 사례를 래프 해제할 수 있습니다.
let someFunctionUsingShaderProgram (ShaderProgram id) =
// Use the unwrapped value
...
구조체 구분된 공용체
타입이 구분된 유니언을 구조체로 나타낼 수도 있습니다. 이 작업은 [<Struct>]
특성으로 수행됩니다.
[<Struct>]
type SingleCase = Case of string
[<Struct>]
type Multicase =
| Case1 of string
| Case2 of int
| Case3 of double
이러한 형식은 참조 형식이 아니라 값 형식이므로 참조 구분된 공용 구조체와 비교하여 추가 고려 사항이 있습니다.
- 값 형식으로 복사되고 값 형식 의미 체계가 있습니다.
- 다중 시 구조체 구분 공용 구조체에는 재귀 형식 정의를 사용할 수 없습니다.
F# 9 이전에는 각 케이스에 고유한 케이스 이름을 유니언 내에서 지정해야 했습니다. F# 9부터 제한 사항이 해제됩니다.
개체 계층 대신 식별된 합집합 사용
종종 판별된 유니온을 작은 객체 계층 구조의 보다 간단한 대안으로 사용할 수 있습니다. 예를 들어, 원, 정사각형 등에 대한 파생 형식이 있는 Shape
기본 클래스를 대신하여 다음과 같은 구분된 공용 구조체를 사용할 수 있습니다.
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
개체 지향 구현에서 사용하는 것처럼 영역 또는 경계를 계산하는 가상 메서드 대신 분기와 패턴 일치를 사용하여 적절한 수식을 사용하여 이러한 수량을 계산할 수 있습니다. 다음 예제에서는 셰이프에 따라 영역을 계산하는 데 여러 수식이 사용됩니다.
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)
출력은 다음과 같습니다.
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
트리 데이터 구조에 판별 합집합 사용
차별화된 합집합은 재귀적일 수 있으며, 이는 합집합 자체가 하나 이상의 케이스 타입에 포함될 수 있음을 의미합니다. 재귀 구분 합집합을 사용하여 프로그래밍 언어에서 식을 모델링하는 데 사용되는 트리 구조를 만들 수 있습니다. 다음 코드에서는 재귀 구분합을 사용하여 이진 트리 데이터 구조를 구성합니다. 공용 구조체는 정수 값과 왼쪽 및 오른쪽 하위 트리가 있는 노드인 Node
및 트리를 종료하는 Tip
두 가지 사례로 구성됩니다.
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
이전 코드에서 resultSumTree
값은 10입니다. 다음 그림에서는 myTree
대한 트리 구조를 보여 줍니다.
myTree의 트리 구조를 보여 주는
트리의 노드가 이질적인 경우 차별된 공용 구조체가 잘 작동합니다. 다음 코드에서 Expression
형식은 숫자와 변수의 추가 및 곱셈을 지원하는 간단한 프로그래밍 언어로 식의 추상 구문 트리를 나타냅니다. 일부 유니온 사례는 재귀적이지 않으며 숫자(Number
) 또는 변수(Variable
)를 나타냅니다. 다른 경우는 재귀적이며, 그 경우 연산(Add
및 Multiply
)은 피연산자도 식인 것을 나타낸다.
Evaluate
함수는 일치 식을 사용하여 구문 트리를 재귀적으로 처리합니다.
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
이 코드가 실행되면 result
값은 5입니다.
회원
차별된 노조에 대한 구성원을 정의할 수 있습니다. 다음 예제에서는 속성을 정의하고 인터페이스를 구현하는 방법을 보여줍니다.
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*
속성
F# 9부터, 차별 유니언은 각 사례에 대해 자동으로 생성된 .Is*
속성을 제공하므로, 값이 특정 사례인지 확인할 수 있습니다.
사용할 수 있는 방법은 다음과 같습니다.
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
일반적인 특성
다음 특성은 일반적으로 차별된 공용 구조체에서 볼 수 있습니다.
[<RequireQualifiedAccess>]
[<NoEquality>]
[<NoComparison>]
[<Struct>]
참조
.NET