Swift-Language Guide 5.7 / Enumeration (야매 번역 + 정리)

Newon·2022년 8월 4일
0

Swift-Language Guide 5.7

목록 보기
6/7
post-thumbnail

초록

열거형 (Enumeration) 은 관련이 있는 값들을 하나로 묶은 타입(자료형) 으로, 코드 상에서 개발자가 만든 자료형을 안전하게 사용할 수 있게 해줍니다.

열거형의 케이스들은 특정 데이터들을 담는 기능이 포함되어 있습니다. 이때 특정 데이터들은 어떠한 자료형이라도 케이스들이 저장할 수 있으며, 케이스 별로 서로 다른 데이터들을 포함할 수도 있습니다.

Swift 에서 열거형은 일급객체입니다. 열거형들은 클래스에서 지원하는 다양한 기능들을 지원하는데 연산 속성(computed properties) 를 통해 열거형의 현재 값에 대한 추가적인 정보를 제공하거나, 열거형에 속한 값을 함수로 제공하기 위해 메소드를 인스턴스하는 기능등이 포함됩니다.

일급객체 (First - Class)
: 스위프트에서 일급객체란

  • 상수(let)나 변수(var) 에 저장될 수 있습니다.
  • 다른 함수에 인자로써 전달할 수 있습니다.
  • 다른 함수의 리턴값으로 반환할 수 있습니다.

3개의 특징을 지닌 type 을 의미합니다. 출처


열거형 문법 (Enumeration Syntax)

열거형은 enum 키워드를 사용하여 정의하고, 그 내부에 내용을 작성할 수 있습니다.
이때 내부의 내용이 여러개라면 쉼표를 통해 구분할 수도 있습니다.

enum CompassPoint {
	case north
    case south
    case east
    case west
}

enum Planet {
	case mercury, venus, earth, mars, jupiter, 
    saturn, uranus, neptune
}

Note:
다른 언어들과 달리, 스위프트의 열거형은 정수값을 기본적으로 선언하지 않습니다.

CompassPoint 의 예시를 보면 C 언어와 달리 north, south, east, west 는 각각 0, 1, 2, 3 으로 암시되지 않습니다. 그 대신, 열거형의 케이스들은 그 자체로써 자료형으로 정의됩니다.


각각의 열거형들은 새로운 자료형을 정의하는 것과 같습니다. 따라서 스위프트의 다른 자료형들처럼 대문자로 시작합니다. 열거형의 자료형은 단수형으로 지음으로써 이름 그 자체로써 이해하기 쉽도록 작성하는 것이 좋습니다.

어떤 변수가 한번 열거형으로 선언된다면 해당 변수는 새로운 값을 받을 때,
암시적으로 이전에 사용했던 열거형으로 선언될 것을 알 수 있습니다.
그렇기에 .case 의 형태로 짧게 표현하는 것도 가능합니다.

var directionToHead = CompoassPoint.west
directionToHead = .east

Switch 구문에서 열거형의 값 사용하기 (Matching Enumeration Values with a Switch Statement)

각각의 열거형 값들을 switch 구문에서 사용할 수도 있습니다.
Control Flow 에서 언급하였듯, switch 구문은 반드시 모든 상황을 고려해야만 합니다.

이때 열거형을 사용하면, 열거형의 모든 케이스들에 각각 상황을 설정하였다면 default 구문을 사용하지 않아도 컴파일에서 오류가 발생하지 않습니다. 모든 상황을 고려했기 때문입니다. 반대로 열거형의 하나의 케이스라도 설정하지 않았다면 컴파일에서는 오류 구문을 발생시킵니다.

enum CompassPoint {
	case north
    case south
    case east
    case west
}

let directionToHead = CompassPoint.east

switch directionToHead {
case .north: 
	print("북쪽엔 다양한 행성들이 있어요!") 
case .south: 
	print("펭귄 조심하세요 ~") 
case .east: 
	print("태양이 떠오르는 방향이라네") 
case .west: 
	print("하늘이 푸르른 곳이라네") 
}

switch directionToHead {
case .north:
	print("북쪽엔 다양한 행성들이 있어요!")
default:
	print("동, 서, 남에는 다른 것들이 있죠!")
}

열거형 케이스들을 순회하여 사용하기 (Iterating over Enumeration Cases)

때때로 열거형들은 컬렉션처럼 순회하며 사용하는 것이 유용할 경우가 있습니다. 이때 열거형 뒤에 : CaseIterable 을 붙임으로써 얼거형을 컬렉션처럼 사용할 수 있습니다. 스위프트는 해당 열거형의 allCases 속성을 통해 해당 컬렉션들을 순회하며 보여주게 됩니다.

enum Beverage: CaseIterable {
	case coffe, tea, juice
}

let numberOfChoices = BeverageAllcases.count
print(numberOfChoices)

for beverage in Beverage.allCases {
	print(beverage)
}

// 3
// coffee
// tea
// juice

: CaseIterable 은 프로토콜입니다.
프로토콜에 대한 정보는 이곳에서 확인할 수 있습니다.


연관값 (Associated Values)

열거형의 케이스 값들에 특정한 데이터들을 함께 저장하는 것이 유용할 때가 있습니다. 이때 저장되는 데이터들을 연관값(associated value) 이라고 합니다.

스위프트 열거형에서 연관값은 어떠한 자료형의 값이라도 저장할 수 있으며, 하나의 열거형에서 연관값들의 자료형이 케이스마다 다르더라도 상관없습니다.


바코드를 통해 예시를 들 수 있습니다.
물품 추적 관리 시스템이 물건들을 관리할 때, 2개 종류의 바코드를 사용한다고 가정해봅니다.

첫번째 종류는 UPC 형식으로, 0 - 9 까지의 숫자를 사용하는 바코드입니다. 각 바코드들은 1자리의 시스템 숫자, 공장 번호 5자리 숫자, 물품 번호 5자리 숫자, 마지막으로 옳게 스캔되었는지 확인하는 1자리의 확인 숫자가 일렬로 늘어져있는 형태입니다.


두번째 종류는 QR 코드 방식으로 ISO 8859-1 방식의 문자를 2953 자까지 저장할 수 있는 형태입니다.


이런 경우 바코드 열거형을 생성한 후 UPC 형태는 4개의 정수를 가진 튜플로 지정하고, QR 코드는 길이 자유의 스트링으로 지정하는 것이 편리합니다.

코드로 표현하면 다음과 같아집니다.

enum Barcode {
	case upc(Int, Int, Int, Int)
    case qrCode(String)
}

var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOPQRSTUWXYZ")

이때 하나의 변수인 productBarcode
.upc 일 때는 4개의 정수로 이루어진 튜플로,
.qrCode 일 때는 스트링으로 이루어진 것을 확인할 수 있으며
자유롭게 변형이 가능한 것을 확인할 수 있습니다.

다만 이때, 하나의 변수가 upcqrCode 두 가지 방식 모두를 포함할 수는 없습니다. 변수는 초기화되는 단 한번의 순간에, 하나만 저장할 수 있기 때문입니다.


위의 열거형에서 연관값들은 let, var 의 형태와 함께 각각 그 이름을 설정해줄 수 있습니다.
연관값들이 모두 let 이나 var 로 통일된다면 해당 선언은 case 바로 옆으로 빼서 사용할 수도 있습니다.

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).")
}

switch productBarcode {
	case let .upc(numberSystem, manufacturer, product, check):
    	print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
    case let .qrCode(productCode):
    	print("QR code: \(productCode).")
}

위의 예시는 switch 에서 사용하였지만, enum 선언에서도 동일하게 사용할 수 있습니다.


원시값(Raw Values)

열거형 케이스들은 기본 값을 통해 사전에 값을 가질 수 있으며, 이때 사용되는 값들은 모두 동일한 자료형이어야 합니다. 그리고 이것을 원시값 이라고 지칭합니다.

enum ASCIIControlCharacter: Character {
	case tab = "\t"
    case lineFeed - "\n"
    case carriageReturn - "\r"
}

위의 예시는 아스키 컨트롤 문자들을 좀 더 사용하기 쉽도록 문자로 기본값을 설정한 열거형입니다.

이처럼 원시값은 문자열, 문자, 혹은 정수형이나 부동 소수점으로 정할 수 있습니다. 각 원시값들은 하나의 열거형내에서 고유해야합니다.

Note:
연관값과 원시값은 다른 용도로 사용됩니다.
원시값은 같은 열거형이면 언제나 같은 케이스를 지칭하지만,

연관값은 같은 열거형이여도 다른 케이스를 지칭할 수 있으며,
사실 케이스를 설명할 뿐 케이스를 지칭하지도 않습니다.

암시적 원시값 할당 (Implicitly Assigned Raw Values)

열거형을 정수나 스트링등의 원시값으로 설정할 때, 모든 케이스마다 일일히 값을 설정할 필요 없습니다. 하나만 설정하면, 스위프트가 알아서 값을 할당해줍니다.

enum Planet: Int {
	case mercuy = 1, venus, earth, mars, jupiter ...
}

위의 예시의 경우 mercury 가 1이므로, venus 부터 2, 3, 4 로 증가하며 원시값이 할당됩니다. 한편 특정 값을 설정하지 않고, : Int 를 사용하면 0부터 원시값이 할당됩니다.

원시값을 스트링으로 지정한다면, 각 케이스들의 이름이 원시값으로 할당됩니다.

원시값으로 초기화하기 (Initializing from a Raw Value)

열거형을 원시값과 함께 정의하면,
변수를 선언할 때 열거형의 파라미터로 원시값을 넣어서 초기화할 수 있습니다.
(이때 파라미터의 이름은 rawValue 입니다.)

let possiblePlanet = Plaent(rawValue: 7)

이때 원시값을 제공하더라도, 해당 원시값이 열거형에 존재하는지 확신할 수 없기 때문에 원시값을 활용한 초기화는 옵셔널 타입으로 반환됩니다.

재귀 열거형 (Recursive Enumerations)

재귀 열거형은 열거형에서 자기 자신을 다시 호출하는 형태를 의미합니다.

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

(5 + 4) * 2 를 열거형을 통해 표현하고 싶을 때,

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

let two = ArithmeticExpression.number(2)
let product = ArithmeticExpression.addition(sum, two)

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 .multiplecation(left, right):
    	return evaluate(left) * evaluate(right)
    }
}

print(evaluate(product))
// 18

처럼 사용할 수 있습니다.
..이걸? 굳이?

profile
나만 고양이 없어

0개의 댓글