[Swift] 순환(재귀) 열거형 - Recursive Enumerations

오늘은 야곰님의 Swift 프로그래밍 책을 보며 처음 보는 열거형이 있어서 정리하는 글을 작성하려고 한다.

1. 순환 열거형 이란?

순환 열거형은 열거형 케이스의 연관 값이 열거형 자신의 값이고자 할 때 사용합니다.
재귀적으로 해당 열거형 케이스를 반복하게 되므로 `indirect` 키워드를 사용하여 명시하도록 한다.

Swift에서 열거형은 n개의 case를 생성할 수 있고 그 case는 연관 값(parameter)를 가질 수 있다는 건 알고 있을 것이다.

위 말을 풀어보면 case의 parmeter로 `자기 자신`이 사용된다는 의미이다.

텍스트만 보면 이해가 잘 안될 수도 있는데(내가 그랬음) 예제를 하나하나 뜯어보면 쉽게 알 수 있다.

2. 예제

다음은 간단한 산술 표현식을 저장하는 열거형이다.

이 열거형은 숫자, 2개의 표현식의 덧셈, 2개의 표현식의 곱셈의 3가지의 산술 표현식을 저장할 수 있다.

2-1. 열거형

enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}

위 예제의 경우, indirect 키워드를 `addition` `multiplication` case 앞에 명시하여 두 case만 한정해 순환 열거형을 만든 것이다.

열거형 연관 값이 자기 자신(ArithmeticExpression)이기 때문에 indirect 키워드를 사용해 순환 열거형으로 만들 수 있다.

indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}

위 예제의 경우엔 indirect 키워드를 enum 앞에 명시하여 ArithmeticExpression 열거형 전체를 순환 열거형으로 만들었다는 차이가 있다.

2-2. 예제

let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

func evaluate(_ expression: ArithmeticExpression) -> Int {
    switch expression {
    case let .number(value):
        return value
    case let .addition(left, right):
        return evaluate(left) + evaluate(right)
    case let .multiplication(left, right):
        return evaluate(left) * evaluate(right)
    }
}

print(evaluate(product))
// Prints "18"

ArithmeticExpression 열거형을 사용해 (5 + 4) X 2 연산을 계산하는 예제입니다.

  • `five` 열거형에서 사용될 정수 '5'
  • `four` 열거형에서 사용될 정수 '4'
  • `sum` addition case로 만든 덧셈 표현식 → (5 + 4)에 해당하는 좌항
  • `product` multiplication case로 만든 곱셉 표현식 → sum * 2에 해당하는 우항
  • `evaluate` 순환 열거형의 계산을 도와주는 순환 함수

2-3. 전체 코드

더보기
indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}

let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

func evaluate(_ expression: ArithmeticExpression) -> Int {
    switch expression {
    case let .number(value):
        return value
    case let .addition(left, right):
        return evaluate(left) + evaluate(right)
    case let .multiplication(left, right):
        return evaluate(left) * evaluate(right)
    }
}

print(evaluate(product))
// Prints "18"