열거형은 어떤 값들을 group으로 묶어 경우의 수를 제한하는 타입이다
개념적으로는 C언어의 것과 유사하나
Swift에서는 각 case에 정수 외의 String/Float 등을, 심지어 여러 개도 할당할 수 있다는 차이가 있다
연관값(associated value)이라는 개념으로 case마다 다양한 타입과 여러 개의 값을 저장시킬 수 있다
Swift의 열거형은 계산 프로퍼티, 인스턴스 메소드, 생성자 등도 사용할 수 있다
익스텐션과 프로토콜도 적용할 수 있다
목차
1. 열거형의 구문
2. 열거형 + switch문 조합 시 유의사항
3. 모든 case 루프 돌리기
4. 연관값 (Associated Value)
5. Raw Value
6. 순환 열거형 (Recursive Enumerations)
열거형 구문
기본형태는 아래와 같다
enum CompassPoint {
case north
case south
case east
case west
}
//이어 쓸 수도 있다
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
나침반을 예시로 4가지 열거형 case를 선언하였다
✅ 주의
Swift에선 C언어와 달리, 각 case에 암시적으로 정수가 할당되지 않는다.
정수 대신 case의 이름 자체가 할당된다. (ex. north, south ...)
열거형 타입을 할당하는 예시
var directionToHead = CompassPoint.west
directionToHead = .east
한 번 타입이 설정되면 CompassPoint라는 타입명은 생략해도 된다
열거형 + switch문 조합
열거형 타입을 Switch의 조건값으로 줄 때는 case를 전부 명시하거나 default를 줘야한다
enum CompassPoint {
case north
case south
case east
case west
}
directionToHead = .south
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
// Prints "Watch out for penguins"
열거형 case 루프 돌리기
다루고 있는 열거형 타입이 CaseIterable 프로토콜을 채택하게 함으로써 case 루프를 돌릴 수 있다
채택 후 .allCases 프로퍼티를 통해 case를 Collection 타입으로 뽑아낸다
enum Beverage: CaseIterable {
case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
// Prints "3 beverages available"
for beverage in Beverage.allCases {
print(beverage)
}
// coffee
// tea
// juice
※ 블로그 출처라 정확하진 않지만,,, 프로토콜 채택만 하면 구현은 "컴파일러"가 자동으로 제공한다고 한다
연관값 (Associated Values)
정의
각 case에 값을 할당하는 2가지 방법 중 하나인 연관값이다.
연관값은 열거형 인스턴스마다 다른 값을 가지는, 일종의 인스턴스 프로퍼티처럼 생각할 수 있다.
또한, case마다 다양한 타입의 값을 심지어 여러 개를 할당할 수 있다
✅ 연관값의 타입은 튜플이며 내부 타입 구성은 서로 달라도 무관하다
예제 - 선언 & 할당
덕분에 이런 구현이 가능하다.
예로)
마트에서 상품들을 추적하는 시스템을 만드는데 바코드가 두 종류가 있는데
바코드의 종류에 따라 이를 번역하는 방법이 매우 다른 상황이다
[ UPC 타입 바코드 ]
[ QR코드 타입 바코드 ]
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
연관값은 이름이 없고 타입만 명시해준다
예제 - 연관값 추출
switch문으로 추출할 수 있다
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
// 전부 상수 or 전부 변수로 추출하는 경우라면
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
✅ 이 때, 연관값은 값타입으로 추출되므로
변수로 선언하여 값을 변경하더라도 원본은 바뀌지 않는다
Raw Values (=디폴트값)
정의
case에 값을 할당하는 두번째 방법인 Raw value이다
Raw value는 모든 인스턴스가 공유하는 일종의 타입 프로퍼티로 생각할 수 있고
런타임이 아닌 디폴트값으로만 설정할 수 있다
연관값과 달리, case마다 하나씩만 저장할 수 있고 모든 case가 같은 타입을 사용해야 한다
열거형 타입 이름 옆에 Raw value의 타입을 명시해주어야 한다
구문 간단하게 쓰기
#1. 첫번째 case에 1을 주면 나머지는 자동으로 2부터 할당된다
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
#2. 타입을 String으로 지정하면 case 이름 그대로 할당한다
enum CompassPoint: String {
case north, south, east, west
}
// "north", "south", "east", "west"
case 이름이 아닌 Raw Value로 역추적하여 생성하기
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus
1대1 대응이라서 이런 생성자도 제공된다
참고로, rawValue에 해당하는 case가 없을 수 있으므로 Optional 생성자이다.
대응되는 case가 없는 예제를 하나보면,
let positionToFind = 11
if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
} else {
print("There isn't a planet at position \(positionToFind)")
}
// Prints "There isn't a planet at position 11"
순환 열거형 (Recursive Enumerations)
정의
자기자신 열거형 타입의 인스턴스를 연관값으로 갖는 열거형을 말한다.
indirect 키워드를 enum 앞에나 case 앞에 명시하여 순환 열거형 case임을 알린다
// 둘 다 가능
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)
}
// 열거형 인스턴스가 마구 쌓인다..
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
// product에 사실상 3개 이상의 인스턴스가 담기게 되었다. 무제한 가능할듯
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"
evaluate이라는 함수 하나로 Recursive 구조를 구현해버렸다
'Swift > Language Guide' 카테고리의 다른 글
Inheritance (상속) (0) | 2021.09.18 |
---|---|
Subscripts (0) | 2021.09.17 |
Closures (0) | 2021.09.15 |
Error Handling - #1/1 (0) | 2021.09.13 |
Access Control - #1/1 (0) | 2021.09.13 |
댓글