[swift] 열거형Enumerations

김개발소발·2022년 2월 23일
0

Swift는 C의 Enumeration보다 유연하다. 각 케이스가 값을 제공할 필요가 없다. 각 케이스 별 값을 제공해야 된다면 데이터의 타입은 자유롭게 설정이 가능하다. (C는 각 케이스 별 정수 값을 반환해야 한다.)

인스턴스 메소드를 정의할 수 있고, 각 케이스 별 초기화 블럭을 정할 수 있다.

Syntax

enum [NAME] {
	case [CASE_NAME]
}

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

// case 문 뒤에 콤마로 각 케이스를 선언할 수 있다.
enum CompassPoint {
	case north, south, east, west
}

var directionToHead = CompassPoint.west
// directionToHead의 타입은 이미 알 수 있게 때문에 CompassPoint(enum의 이름)을 생략할 수 있다.
var directionToHead = .west

스위프트의 Enumeration은 C언어와 다르게 각 케이스는 기본 정수 값을 가지지 않는다.

Associated Values

각 케이스 별 다른 타입, 갯수를 정의할 수 있다. 그것을 associated values라고 한다.

예를 들어,

바코드는 1D의 0~9까지 숫자로 제품을 구분하고,
QR 코드는 2D로 ISO 8859-1 문자로 최대 2953자까지 표현할수 있다.

UPC바코드는 4자의 숫자로 구성되고, QR 코드는 문자열(String)으로 구성된다.

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

upc케이스는 4개의 정수를 associated values, qrCode는 String 타입의 associated value를 정의했다.

// 각 케이스를 초기화 할 때는 선언된 associated values의 타입에 맞춰 값을 넘겨주면 된다.

// upc는 Int, Int, Int, Int로 associated values가 정의되었다.
var productBarcode = Barcode.upc(8, 85909, 51226, 3)

// qrCode는 String associated values가 정의되었다.
productBarcode = Barcode.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: \(productCode).")
}
//print "QR: ABCDEFGHIJKLMNOP."

각 케이스 별 모든 associated values를 상수(let) 혹은 변수(var)로 추출할 경우 case 키워드 다음 var 나 let 키워드를 추가하면 된다.

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

Raw Value

associated value는 각 케이스별 다른 데이터로 정의할 수 있지만, 각 케이스를 구분할 수 있는 한 타입으로 미리 정의된 값을 가질 수 있다. 이 값을 rawValue라고 한다.

/*
enum NAME:RAW_VALUE {
	case CASE_NAME
}
*/

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

ASCIIControlCharacterCharacter타입의 rawValue를 가진다.

tab은 “\t”
line은 “\n”
carriageReturn은 “\r”의 rawValue를 가진다.

각 rawValue는 문자, 문자열, 정수, 소수 타입으로 정의할 수 있고, 한 enum 내에서 유일하다.

associated value 와 rawValue의 특징

  1. rawValue는 생성되면서 정의되고, associated value는 각 케이스가 초기화 될 때 정의 된다.
  2. enum의 값이 변경되어도 rawValue는 변경되지 않지만, associated value는 변경 가능하다.

Implicitly Assigned Raw Values

enum은 Int, String 타입의 rawValue를 지정하지 않으면
Int는 0부터

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}

Planetmercury의 rawValue를 1로 지정하면, venus의 rawValue는 2가 되고, 다음은 3 과같이 지정된다.

enum CompassPoint: String {
    case north, south, east, west
}

String은 case의 이름으로 지정된다.

Initializing from a Raw Value

rawValue를 통해 enum을 초기화 할 수 있다. 단 옵셔널로 초기화 된다.

// enum(rawValue:VALUE)
let possiblePlat = Planet(rawValue:7) // 7 == Uranus

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

enumeration의 associated value를 가지는 enumeration

재귀를 표시하기 위해 case 앞에 indirect 케이스를 추가한다.

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

하나 이상의 재귀 케이스가 포함된 enum은 enum키워드 앞에 사용할 수 있다.

(5 + 4) * 2 연산을 recursive enumeration을 사용한 코드

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"

number는 정수형을 반환하고, addition, multiplication 케이스는 다시 evaluate 함수에 ArithmeticExpression 를 파리미터로 재호출 한다.
evaluate 함수는 Int(정수) 값을 반환하기 때문에 number 케이스의 값을 반환하여 다른 케이스의 연산 결과를 반환한다.

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

코드의 sumevaluate(_ expression: ArithmeticExpression)함수의 파라미터로 전달한다.

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

먼저 switch 문에서 addition 케이스를 타게되고, addition 케이스의 association value 타입인 ArithmeticExpression을 다시 evaluate 메소드를 호출한다.
이 함수는 number 케이스의 정수형 값을 넘겨줄 때까지 재귀호출 형식으로 호출해서 최종적으로 연산된 값을 반환한다.

profile
사람들 속에 숨어사는 INTJ 성향을 가진 개발자

0개의 댓글