티스토리 뷰

1. CaseIterable의 allCases를 초간단하게 보고

2. 거기서 reduce와 map의 조합을 볼 생각

 

Swift의 열거형은 enum 이라는 키워드로 선언할 수 있다.

그리고 이 enum은 CaseIterable 프로토콜을 채택할 수 있는데, 그러면 allCases라는 이름의 타입 프로퍼티를 통해 enum의 모든 케이스의 컬렉션을 생성해준다.

 

enum School: CaseIterable {
    case elementary
    case middle
    case high
}

let allCases: [School] = School.allCases
print(allCases)
// [School.elementary, School.middle, School.high]

(이런식으로)

 

그리고 이 글 쓰게 한 예시는..

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(withSauce: 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)
print(MainDish.allCases)

이건데

14

[MainDish.pasta(taste: PastaTaste.cream), MainDish.pasta(taste: PastaTaste.tomato),
 MainDish.pizza(dough: PizzaDough.cheeseCrust, topping: PizzaTopping.pepperoni),
 MainDish.pizza(dough: PizzaDough.cheeseCrust, topping: PizzaTopping.cheese),
 MainDish.pizza(dough: PizzaDough.cheeseCrust, topping: PizzaTopping.bacon), 
 MainDish.pizza(dough: PizzaDough.thin, topping: PizzaTopping.pepperoni), 
 MainDish.pizza(dough: PizzaDough.thin, topping: PizzaTopping.cheese), 
 MainDish.pizza(dough: PizzaDough.thin, topping: PizzaTopping.bacon), 
 MainDish.pizza(dough: PizzaDough.original, topping: PizzaTopping.pepperoni), 
 MainDish.pizza(dough: PizzaDough.original, topping: PizzaTopping.cheese), 
 MainDish.pizza(dough: PizzaDough.original, topping: PizzaTopping.bacon), 
 MainDish.chicken(withSauce: true), MainDish.chicken(withSauce: false), 
 MainDish.rice]

출력값은 이렇다.

 

여기서 이해가 잘 안 갔던건

PizzaDough.allCases.reduce([]) { (result, dough) -> [MainDish] in
	result + PizzaTopping.allCases.map { (topping) -> MainDish in
    MainDish.pizza(dough: dough, topping: topping)};
}

이 부분  😱

 

allCases, reduce, map 따로는 이해가는데 합치니까 이해 안 감.

그래서 뜯어보자..

 

reduce, map 은 어느정도 이해했다는 가정하에)

 

우선 안부터 봐야 하는데

PizzaTopping.allCases.map { (topping) -> MainDish in
	MainDish.pizza(dough: dough, topping: topping)};

이거.

 

-> 출력값 얘기할 땐 편의상 한국어로 작성

 

위 코드의 map 되는 과정을 하나씩 밟아보자.

 

피자토핑.allCases = [피자토핑.페퍼로니, 피자토핑.치즈, 피자토핑.베이컨]

 

이렇게 나올텐데 이 배열의 "원소" 하나를 topping이라고 이름 짓고 이걸 input

그리고 그 input이 map을 거쳐 나오는 output/결과값이 MainDish 타입으로 나옴.

 

topping -> map -> MainDish
어떻게 map 할건데 ? -> MainDish.pizza(dough: dough, topping: topping) 모양으로 ㅇㅇ

 

 

topping은 순서대로 페퍼로니, 치즈, 베이컨이 될 걸 아는데

dough는 어디서 오는거야 하고 보니까 바깥에

PizzaDough.allCases.reduce([]) { (result, dough) -> [MainDish] in

여기서 오고 있음 😭

 

그래서 map을 보다말고 reduce를 보러 가게 됨 🏃🏻‍♀️

한 단계씩 보자

 

PizzaDough.allCases = [피자도우.치즈크러스트, 피자도우.씬, 피자도우.오리지널]

 

reduce는 (result, dough)가 들어가서 [MainDish] 배열로 나오는 함수임.

근데 잘 보면

reduce([]) 로 초기값을 []로 주고 있다. => result의 첫 값이 []로 빈 배열임.

그러면

첫 단계에서

result = [] dough = 피자도우.치즈크러스트 가 됨.

 

난 아직 첫 단계에 있다는 걸 잊지말고

...

 

reduce의 첫 단계의 dough = 피자도우.치즈크러스트들고 다시 map으로 들어가서

(topping) -> MainDish in MainDish.pizza(dough: dough, topping: topping)

여기 적용한다

 

그러면 map의 결과값으로

[MainDish.pizza(dough: 피자도우.치즈크러스트, topping: 피자토핑.페퍼로니),

MainDish.pizza(dough: 피자도우.치즈크러스트, topping: 피자토핑.치즈),

MainDish.pizza(dough: 피자도우.치즈크러스트, topping: 피자토핑.베이컨)] 가 나오겠지 ??

 

cf. 배열 map 돌리면 그 안에서는 Int -> Int 라고 표현해도 당연히(?) 전체는 배열이 됨.

Int -> Int 는 안에 원소의 map에 대한 입출력 타입을 설명해주는 것임.

let numbers: [Int] = [0, 1, 2, 3, 4]
var doubleNumbers: [Int] = [Int]()
doubleNumbers = numbers.map({ (number: Int) -> Int in number * 2 })
print(doubleNumbers)
// [0, 2, 4, 6, 8]

 

이 말은 즉슨, reduce 내 코드가

result + PizzaTopping.allCases.map { (topping) -> MainDish in MainDish.pizza(dough: dough, topping: topping)};

=

[] + [MainDish.pizza(dough: 피자도우.치즈크러스트, topping: 피자토핑.페퍼로니), MainDish.pizza(dough: 피자도우.치즈크러스트, topping: 피자토핑.치즈), MainDish.pizza(dough: 피자도우.치즈크러스트, topping: 피자토핑.베이컨)]

이렇게 된다는 이야기 ~

 

그러면 이제 한 바퀴 돈거니까

그 다음의 reduce는

result = [피자도우.치즈크러스트 + 토핑 3가지] dough = 피자도우.씬

이 되어서

위와 같은 과정을 또 거치면

 

그 다음 단계(마지막)에서는

result = [피자도우.치즈크러스트 + 토핑 3가지, 피자도우.씬 + 토핑 3가지] dough = 피자도우.오리지널

 ~~ 해서

 

reduce의 전 과정이 끝났을 땐

[MainDish.pizza(dough: PizzaDough.cheeseCrust, topping: PizzaTopping.pepperoni),
 MainDish.pizza(dough: PizzaDough.cheeseCrust, topping: PizzaTopping.cheese),
 MainDish.pizza(dough: PizzaDough.cheeseCrust, topping: PizzaTopping.bacon), 
 MainDish.pizza(dough: PizzaDough.thin, topping: PizzaTopping.pepperoni), 
 MainDish.pizza(dough: PizzaDough.thin, topping: PizzaTopping.cheese), 
 MainDish.pizza(dough: PizzaDough.thin, topping: PizzaTopping.bacon), 
 MainDish.pizza(dough: PizzaDough.original, topping: PizzaTopping.pepperoni), 
 MainDish.pizza(dough: PizzaDough.original, topping: PizzaTopping.cheese), 
 MainDish.pizza(dough: PizzaDough.original, topping: PizzaTopping.bacon)]

이렇게 되겠죠 ?

 

그러면 ,, 끝 !!

댓글