구별된 공용 구조체

차별된 공용 구조체는 여러 개의 명명된 사례 중 하나일 수 있는 값(각각 다른 값 및 형식)을 지원합니다. 차별된 공용 구조체는 이질적인 데이터에 유용합니다. 유효한 사례 및 오류 사례를 포함하여 특수한 경우를 포함할 수 있는 데이터 한 인스턴스에서 다른 인스턴스로 형식에 따라 달라지는 데이터 작은 개체 계층 구조의 대안으로 사용할 수 있습니다. 또한 재귀 구분된 공용 구조체는 트리 데이터 구조를 나타내는 데 사용됩니다.

구문

[ 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

일반적으로 사례 식별자는 공용 구조체의 이름으로 한정하지 않고 사용할 수 있습니다. 이름이 항상 공용 구조체의 이름으로 정규화되도록 하려면 RequireQualifiedAccess 특성을 공용 구조체 형식 정의에 적용할 수 있습니다.

차별된 노조 래핑 해제

F# 구분 공용 구조체에서는 단일 형식 래핑을 위해 do기본 모델링에 자주 사용됩니다. 패턴 일치를 통해 기본 값을 쉽게 추출할 수 있습니다. 단일 사례에 대해 일치 식을 사용할 필요가 없습니다.

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 Case1 : string
    | Case2 of Case2 : int
    | Case3 of Case3 : double

이러한 형식은 참조 형식이 아니라 값 형식이므로 참조 구분된 공용 구조체와 비교하여 추가 고려 사항이 있습니다.

  1. 값 형식으로 복사되고 값 형식 의미 체계가 있습니다.
  2. 다중 시 구조체 구분 공용 구조체에는 재귀 형식 정의를 사용할 수 없습니다.
  3. 다중 시 구조체 구분 공용 구조체에 고유한 대/소문자 이름을 제공해야 합니다.

개체 계층 대신 차별된 공용 구조체 사용

종종 구분된 공용 구조체를 작은 개체 계층 구조의 더 간단한 대안으로 사용할 수 있습니다. 예를 들어 원, 정사각형 등의 파생 형식이 있는 기본 클래스 대신 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

트리 데이터 구조에 대해 차별된 공용 구조체 사용

차별된 공용 구조체는 재귀적일 수 있으며, 이는 노조 자체가 하나 이상의 사례 유형에 포함될 수 있음을 의미합니다. 재귀 구분된 공용 구조체를 사용하여 프로그래밍 언어로 식을 모델링하는 데 사용되는 트리 구조를 만들 수 있습니다. 다음 코드에서는 재귀 구분된 공용 구조체를 사용하여 이진 트리 데이터 구조를 만듭니다. 공용 구조체는 정수 값과 왼쪽 및 오른쪽 하위 트리Tip가 있는 노드이고 트리를 종료하는 두 가지 사례Node로 구성됩니다.

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트리 구조를 보여 줍니다.

Diagram that shows the tree structure for myTree.

트리의 노드가 이질적인 경우 차별된 공용 구조체가 잘 작동합니다. 다음 코드에서 이 형식 Expression 은 숫자와 변수의 추가 및 곱셈을 지원하는 간단한 프로그래밍 언어로 식의 추상 구문 트리를 나타냅니다. 일부 공용 구조체 사례는 재귀적이지 않으며 숫자() 또는 변수(NumberVariable)를 나타냅니다. 다른 경우는 재귀적이며 피연산자도 식인 연산(AddMultiply)을 나타냅니다. 함수는 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}"

일반적인 특성

다음 특성은 일반적으로 차별된 공용 구조체에서 볼 수 있습니다.

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

참고 항목