Swift

[Swift] 열거형 ( 항목 순회, 순환 열거형 )

yevdev 2022. 3. 15. 14:20

연관 값을 같는 열거형

enum MainDish {
    case pasta(taste: String)
    case pizza(dough: String, topping: String)
    case chicken(withSource: Bool)
    case rice
}

var dinner: MainDish = MainDish.pasta(taste: "크림")
print(dinner)
dinner = .pizza(dough: "치즈크러스트", topping: "불고기")
print(dinner)
dinner = .chicken(withSource: true)
print(dinner)
dinner = .rice
print(dinner)

// pasta(taste: "크림")
// pizza(dough: "치즈크러스트", topping: "불고기")
// chicken(withSource: true)
// rice

 

여러 열거형의 응용

enum PastaTaste{
    case cream, tomato
}

enum PizzaDough{
    case cheeseCrust, thin, original
}

enum PizzaTopping{
    case pepperoni, cheese, bacon
}

enum MainDish{
    case pasta(taste: PastaTaste)
    case pizza(dough: PizzaDough, topping: PizzaTopping)
    case chicken(withSource: Bool)
    case rice
}

var dinner: MainDish = MainDish.pasta(taste: PastaTaste.tomato)
print(dinner)

dinner = MainDish.pizza(dough: PizzaDough.cheeseCrust, topping: PizzaTopping.bacon)
print(dinner)


// pasta(taste: __lldb_expr_17.PastaTaste.tomato)
// pizza(dough: __lldb_expr_17.PizzaDough.cheeseCrust, topping: __lldb_expr_17.PizzaTopping.bacon)

 

CaseIterable 프로토콜을 활용한 열거형의 항목 순회

avaliable 속성을 갖는 열거형의 항목 순회 (allCases 프로퍼티 사용)

enum School: String, CaseIterable {
    case primary = "유치원"
    case elementary = "초등학교"
    case middle = "중학교"
    case high = "고등학교"
    case college = "대학"
    case universary = "대학교"
    @available(iOS, obsoleted: 12.0)
    case graduate = "대학원"
    
    static var allCases: [School] {
        let all: [School] = [.primary,
                             .elementary,
                             .middle,
                             .high,
                             .college,
                             .universary]
        #if os(iOS)
        return all
        #else
        return all + [.graduate]
        #endif
    }
}

let allCases: [School] = School.allCases
print(allCases)
// 실행 환경에 따라 다른 결과
// 일단 내 환경에서는
// [__lldb_expr_19.School.primary, __lldb_expr_19.School.elementary, __lldb_expr_19.School.middle, __lldb_expr_19.School.high, __lldb_expr_19.School.college, __lldb_expr_19.School.universary]
 특정 속성을 통해 특정 케이스를 플랫폼에 따라 사용할 수 있거나 없는 경우가 생기면 CaseIterable 프로토콜을 채택하는 것 만으로는 allCases 프로퍼티를 사용할 수 없다.
 그럴때는 직접 allCases 프로퍼티를 구현해 주어야 한다.

 

연관 값을 같는 열거형의 항목 순회 (CaseIterable 프로토콜을 채택하여도 allCases 프로퍼티를 바로 사용할 수 없는 경우)

enum PastaTaste: CaseIterable {
    case cream, tomato
}

enum PizzaDough: CaseIterable {
    case cheeseCrust, thin, original
}

enum PizzaTopping: CaseIterable {
    case pepperoni, cheese, bacon
}

enum MainDish: CaseIterable{
    case pasta(taste: PastaTaste)
    case pizza(dough: PizzaDough, topping: PizzaTopping)
    case chicken(withSource: Bool)
    case rice
    
    static var allCases: [MainDish] {
        return PastaTaste.allCases.map(MainDish.pasta) + PizzaDough.allCases.reduce([]) { (result, dough) -> [MainDish] in result + PizzaTopping.allCases.map{(topping) -> MainDish in MainDish.pizza(dough: dough, topping: topping)}}
        + [true, false].map(MainDish.chicken)
        + [MainDish.rice]
    }
}

print(MainDish.allCases.count) // 14
print(MainDish.allCases) // 모든 경우의 연관 값을 갖는 케이스 컬렉션
14
[__lldb_expr_21.MainDish.pasta(taste: __lldb_expr_21.PastaTaste.cream), __lldb_expr_21.MainDish.pasta(taste: __lldb_expr_21.PastaTaste.tomato), __lldb_expr_21.MainDish.pizza(dough: __lldb_expr_21.PizzaDough.cheeseCrust, topping: __lldb_expr_21.PizzaTopping.pepperoni), __lldb_expr_21.MainDish.pizza(dough: __lldb_expr_21.PizzaDough.cheeseCrust, topping: __lldb_expr_21.PizzaTopping.cheese), __lldb_expr_21.MainDish.pizza(dough: __lldb_expr_21.PizzaDough.cheeseCrust, topping: __lldb_expr_21.PizzaTopping.bacon), __lldb_expr_21.MainDish.pizza(dough: __lldb_expr_21.PizzaDough.thin, topping: __lldb_expr_21.PizzaTopping.pepperoni), __lldb_expr_21.MainDish.pizza(dough: __lldb_expr_21.PizzaDough.thin, topping: __lldb_expr_21.PizzaTopping.cheese), __lldb_expr_21.MainDish.pizza(dough: __lldb_expr_21.PizzaDough.thin, topping: __lldb_expr_21.PizzaTopping.bacon), __lldb_expr_21.MainDish.pizza(dough: __lldb_expr_21.PizzaDough.original, topping: __lldb_expr_21.PizzaTopping.pepperoni), __lldb_expr_21.MainDish.pizza(dough: __lldb_expr_21.PizzaDough.original, topping: __lldb_expr_21.PizzaTopping.cheese), __lldb_expr_21.MainDish.pizza(dough: __lldb_expr_21.PizzaDough.original, topping: __lldb_expr_21.PizzaTopping.bacon), __lldb_expr_21.MainDish.chicken(withSource: true), __lldb_expr_21.MainDish.chicken(withSource: false), __lldb_expr_21.MainDish.rice]

 

순환 열거형

열거 형 항목의 연관 값이 열거형 자신의 값이고자 할 때 사용

특정 항목에만 한정하고 싶다면 case 키워드 앞에 indirect를 붙이면 되고, 열거형 전체에 적용하고 싶다면 enum 키워드 앞에 indirect 키워드를 붙이면 된다.

// 특정 항목에만 순환 열거형 항목 표시
enum ArithmeticExpression {
    case number(Int)
    indirect case addition(ArithmeticExpression, ArithmeticExpression)
    indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}

// 열거형 전체에 순환 열거형 명시
indirect enum ArithmeticExpression {
    case number(Int)
    case addition(ArithmeticExpression, ArithmeticExpression)
    case multiplication(ArithmeticExpression, ArithmeticExpression)
}

➰ ArithmeticExpression 열거형을 사용하여 (5 + 4) x 2 연산 구현해보기

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 final = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))

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

let result: Int = evaluate(final)
print("(5+4) x 2 = \(result)") //(5+4) x 2 = 18

 


Reference

Swift : 스위프트 프로그래밍 - 야곰