* 내가 읽으려고 내 맘대로 번역한 글.

* 원문 : https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html

 

 

Enumeration

An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code.

열거형은 관련된 값들의 묶음을 위한 공통 타입을 정의하고, 코드에서 타입에 상관없이 값으로 작업할수 있게 해준다.

 

If you are familiar with C, you will know that C enumerations assign related names to a set of integer values. Enumerations in Swift are much more flexible, and don’t have to provide a value for each case of the enumeration. If a value (known as a raw value) is provided for each enumeration case, the value can be a string, a character, or a value of any integer or floating-point type.

너가 c 언어에 친숙하다면, c 열거형이 관련된 이름들을 정수형 값 묶음에 할당하는걸 알것이다.

swift에서 열거형은 더 유연하고, 열거형의 각 요소에 값을 제공하지 않아도 된다.

각 열거형 요소에 값을 제공한다면, 그 값은 문자열, 문자, 정수, 소수점이 될수 있다.

 

Alternatively, enumeration cases can specify associated values of any type to be stored along with each different case value, much as unions or variants do in other languages. You can define a common set of related cases as part of one enumeration, each of which has a different set of values of appropriate types associated with it.

 

Enumerations in Swift are first-class types in their own right. They adopt many features traditionally supported only by classes, such as computed properties to provide additional information about the enumeration’s current value, and instance methods to provide functionality related to the values the enumeration represents. Enumerations can also define initializers to provide an initial case value; can be extended to expand their functionality beyond their original implementation; and can conform to protocols to provide standard functionality.

For more about these capabilities, see Properties, Methods, Initialization, Extensions, and Protocols.

 

Enumeration Syntax

You introduce enumerations with the enum keyword and place their entire definition within a pair of braces:

enum SomeEnumeration {
	// enumeration definition goes here
}

 

Here’s an example for the four main points of a compass:

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

 

The values defined in an enumeration (such as north, south, east, and west) are its enumeration cases. You use the case keyword to introduce new enumeration cases.

 

NOTE

Swift enumeration cases don’t have an integer value set by default, unlike languages like C and Objective-C. In the CompassPoint example above, north, south, east and west don’t implicitly equal 0, 1, 2 and 3. Instead, the different enumeration cases are values in their own right, with an explicitly defined type of CompassPoint.

swift의 열거형은 c나 objective-c와 다르게 기본값으로 정수값을 가지지 않는다.

위의 CompassPoint 예에서, north, south, east, west 는 암시적으로 0, 1, 2, 3과 같지 않다.

대신 다른 열거형 case는 CompassPoint의 명시적으로 정의된 고유한 값이다

 

Multiple cases can appear on a single line, separated by commas:

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

 

Each enumeration definition defines a new type. Like other types in Swift, their names (such as CompassPoint and Planet) start with a capital letter. Give enumeration types singular rather than plural names, so that they read as self-evident:

  1. var directionToHead = CompassPoint.west

The type of directionToHead is inferred when it’s initialized with one of the possible values of CompassPoint. Once directionToHead is declared as a CompassPoint, you can set it to a different CompassPoint value using a shorter dot syntax:

  1. directionToHead = .east

The type of directionToHead is already known, and so you can drop the type when setting its value. This makes for highly readable code when working with explicitly typed enumeration values.

 

Matching Enumeration Values with a Switch Statement

You can match individual enumeration values with a switch statement:

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"

 

You can read this code as:

“Consider the value of directionToHead. In the case where it equals .north, print "Lots of planets have a north". In the case where it equals .south, print "Watch out for penguins".”

…and so on.

 

As described in Control Flow, a switch statement must be exhaustive when considering an enumeration’s cases. If the case for .west is omitted, this code doesn’t compile, because it doesn’t consider the complete list of CompassPoint cases. Requiring exhaustiveness ensures that enumeration cases aren’t accidentally omitted.

switch 문에서 열거형을 사용할때 반드시 포괄적이어야 한다.

.west case를 생략하면, 컴파일 되지 않는다. 왜냐면 CompassPoint case 의 전체목록을 고려하지 않았기 때문에.

이것은 실수로 열거형 case를 빼먹는것을 막아준다.

 

When it isn’t appropriate to provide a case for every enumeration case, you can provide a default case to cover any cases that aren’t addressed explicitly:

모든 열거형 case 에 대해서 기술하지 않으려면, default case 를 사용해서 명시적으로 기술되지 않은 모든 case를 보완해야 한다.

let somePlanet = Planet.earth
switch somePlanet {
case .earth:
	print("Mostly harmless")
default:
	print("Not a safe place for humans")
}
// Prints "Mostly harmless"

 

Iterating over Enumeration Cases

For some enumerations, it’s useful to have a collection of all of that enumeration’s cases. You enable this by writing : CaseIterable after the enumeration’s name. Swift exposes a collection of all the cases as an allCases property of the enumeration type. Here’s an example:

어떨때는 열거형의 모든 case 를 collection 형태로 가지는게 유용할때가 있다.

열거형 이름 뒤에 :CaseIterable 을 붙이면 가능하다.

그러면 swift는 allCases 속성에 모든 case 의 collection을 노출한다.

enum Beverage: CaseIterable {
case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
// Prints "3 beverages available"

 

In the example above, you write Beverage.allCases to access a collection that contains all of the cases of the Beverage enumeration. You can use allCases like any other collection—the collection’s elements are instances of the enumeration type, so in this case they’re Beverage values. The example above counts how many cases there are, and the example below uses a for loop to iterate over all the cases.

위의 예에서, Beverage.allCases 로 Beverage 열거형의 모든 case를 담고 있는 collection에 접근할수 있다.

너는 다른 collection 처럼 allCases 를 사용할수 있는데, 그 collection의 요소들은 열거형 타입의 instance들이기 때문에,

이 경우 그 요소들은 Beverage 값들이다.

위의 예는 case들의 객수를 세고, 아래의 예는 반복문에서 모든 cases들을 사용하고 있다.

for beverage in Beverage.allCases {
	print(beverage)
}
// coffee
// tea
// juice

 

The syntax used in the examples above marks the enumeration as conforming to the CaseIterable protocol. For information about protocols, see Protocols.

 

Associated Values

The examples in the previous section show how the cases of an enumeration are a defined (and typed) value in their own right. You can set a constant or variable to Planet.earth, and check for this value later. However, it’s sometimes useful to be able to store values of other types alongside these case values. This additional information is called an associated value, and it varies each time you use that case as a value in your code.

앞선 예에서 열거형의 case들이 어떻게 자체적으로 값이 정의되었는지 보여주었다.

Planet.earth 에 상수나 변수를 설정할수 있고, 나중에 검사할수 있다.

가끔은 각 case 마다 다른 타입의 값을 저장하는것도 유용하다.

이런 추가된 정보를 연관된 값이라고 부르고, 코드에서 case 를 사용할때마다 다르다.

 

You can define Swift enumerations to store associated values of any given type, and the value types can be different for each case of the enumeration if needed. Enumerations similar to these are known as discriminated unions, tagged unions, or variants in other programming languages.

너는 열거형을 정의하여 관련있는 특정 타입의 값들을 저장할수 있고, 필요하다면 각 열거형의 case 마다 다른 타입일 수도 있다.

열거형은 다른 프로그래밍 언어의 discriminated unions, tagged unions, variants 와 비슷하다 (뭔지 모르겠음)

 

For example, suppose an inventory tracking system needs to track products by two different types of barcode. Some products are labeled with 1D barcodes in UPC format, which uses the numbers 0 to 9. Each barcode has a number system digit, followed by five manufacturer code digits and five product code digits. These are followed by a check digit to verify that the code has been scanned correctly:

예를들어, 2가지 다른 타입의 바코드로 제품을 추적해야 하는 재고추적시스템을 가정하자.

어떤 제품은 0부터 9까지 숫자를 사용하는 UPC 형식의 1D 바코드가 붙어있다.

각 바코드에는 시스템번호가 있고, 그 뒤에 제조업체 코드 5자리와 제품코드 5자리가 있다.

그 뒤에 올바르게 스캔되었는지 검증하는 확인 숫자가 있다.

 

 

Other products are labeled with 2D barcodes in QR code format, which can use any ISO 8859-1 character and can encode a string up to 2,953 characters long:

다른 제품은 ISO 8859-1 문자를 사용하고 최대 2,953 길이까지 인코딩 할수 있는 QR코드 형식의 2D 바코드가 붙어있다.

 

It’s convenient for an inventory tracking system to store UPC barcodes as a tuple of four integers, and QR code barcodes as a string of any length.

재고추적시스템에서 UPC 바코드를 4개의 정수로 이루어진 tuple로 저장하고,

QR코드 바코드는 긴 길이의 문자열로 저장하면 편하다.

 

In Swift, an enumeration to define product barcodes of either type might look like this:

swift에서 두가지 유형의 제품 바코드를 정의하면 다음과 같다.

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

 

This can be read as:

“Define an enumeration type called Barcode, which can take either a value of upc with an associated value of type (Int, Int, Int, Int), or a value of qrCode with an associated value of type String.”

이것은 다음처럼 읽는다.

"Barcode라는 열거형을 정의했고, (int, Int, Int, Int) 타입과 연관된 upc 값을 저장할수 있고,

String 타입과 연관된 qrCode 값도 저장할수 있다."

 

This definition doesn’t provide any actual Int or String values—it just defines the type of associated values that Barcode constants and variables can store when they are equal to Barcode.upc or Barcode.qrCode.

You can then create new barcodes using either type:

정의할때는 실제로 Int 또는 String 값을 넣지 않는다.

단지 Barcode 상수나 변수가 Barcoed.upc 또는 Barcode.qrCode 와 같을때 저장할수 있는 연관된 값을 정의할 뿐이다.

그리고 두가지 유형중 하나를 사용해서 새로운 barcode를 만들수 있다.

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

 

This example creates a new variable called productBarcode and assigns it a value of Barcode.upc with an associated tuple value of (8, 85909, 51226, 3).

You can assign the same product a different type of barcode:

이 예제는 productBarcode 라는 새로운 변수를 만들고, Barcode.upc 값을 연관된 tuple 값으로 설정한다.

같은 제품에 barcode의 다른 타입을 설정할수도 있다.

productBarcode = .qrCode("ABCDEFGHIJKLMNOP")

 

At this point, the original Barcode.upc and its integer values are replaced by the new Barcode.qrCode and its string value. Constants and variables of type Barcode can store either a .upc or a .qrCode (together with their associated values), but they can store only one of them at any given time.

이제 원래 Barcode.upc 의 정수 값들은 새로운 Barcode.qrCode의 문자열 값으로 덮어쓰여 졌다.

Barcode 타입의 상수와 변수는 .upc 또는 .qrCode 두가지 다 저장할수 있다 (연관된 값들과 같이)

하지만 동시에는 안되고, 한번에 하나씩만 저장할수 있다.

 

You can check the different barcode types using a switch statement, similar to the example in Matching Enumeration Values with a Switch Statement. This time, however, the associated values are extracted as part of the switch statement. You extract each associated value as a constant (with the let prefix) or a variable (with the var prefix) for use within the switch case’s body:

switch 문으로 barcode 타입을 검사할수 있다.

연관된 값들을 switch 문에서 받을수 있다.

switch case 의 body 영역에서 연관된 값들을 각각의 상수(let) 또는 변수(var)에 받을수 있다.

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

 

If all of the associated values for an enumeration case are extracted as constants, or if all are extracted as variables, you can place a single var or let annotation before the case name, for brevity:

만약 연관된 값들을 모두 추출하여 상수나 변수에 넣고자 하면,

간략하게 하나의 var 또는 let 키워드만 case name 앞에 붙이면 된다.

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

The barcode example in Associated Values shows how cases of an enumeration can declare that they store associated values of different types. As an alternative to associated values, enumeration cases can come prepopulated with default values (called raw values), which are all of the same type.

Here’s an example that stores raw ASCII values alongside named enumeration cases:

barcode 예제는 어떻게 열거형의 case들이 서로 다른 타입의 연관된 값들을 저장하도록 선언되는지 보여준다.

연관된 값들의 대안으로, 열거형 case에 모두 동일한 타입의 기본값 (원시값)을 채워넣을수 있다.

아래는 원시 ASCII 값들을 열거형 case에 저장하는 예제이다.

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

 

Here, the raw values for an enumeration called ASCIIControlCharacter are defined to be of type Character, and are set to some of the more common ASCII control characters. Character values are described in Strings and Characters.

Raw values can be strings, characters, or any of the integer or floating-point number types. Each raw value must be unique within its enumeration declaration.

ASCIIControlCharacter 라는 열거형의 원시값이 Character 타입으로 정의되었고, 일반적인 ASCII control 문자로 설정되었다.

원시값은 문자열, 문자, 정수, 부동소수점이 될수 있다.

각 원시값은 열거형 선언안에서 유일해야 한다.

 

NOTE

Raw values are not the same as associated values. Raw values are set to prepopulated values when you first define the enumeration in your code, like the three ASCII codes above. The raw value for a particular enumeration case is always the same. Associated values are set when you create a new constant or variable based on one of the enumeration’s cases, and can be different each time you do so.

원시값은 연관된값과 동일하지 않다.

원시값은 너가 코드에서 열거형을 선언할때 값이 미리 채워지는거다.

특정 열거형의 원시값은 항상 동일하다.

연관된 값은 너가 열거형의 case중 하나를 기반으로 새로운 상수나 변수를 만들때 설정되고, 매번 다르게 할수도 있다.

 

Implicitly Assigned Raw Values

When you’re working with enumerations that store integer or string raw values, you don’t have to explicitly assign a raw value for each case. When you don’t, Swift automatically assigns the values for you.

For example, when integers are used for raw values, the implicit value for each case is one more than the previous case. If the first case doesn’t have a value set, its value is 0.

The enumeration below is a refinement of the earlier Planet enumeration, with integer raw values to represent each planet’s order from the sun:

너가 숫자나 문자열을 원시값으로 저장하는 열거형으로 작업할때, 명시적으로 원시값을 매 case 마다 넣어주지 않아도 된다.

넣어주지 않으면 swift가 자동으로 넣어준다.

예를 들어, 숫자를 원시값으로 사용할때, 각 case에 대한 암시적인 값은 이전 case 값보다 1 더 크다.

첫번째 case 에 값을 설정하지 않으면, 0이 된다.

아래 열거형은 Planet 열거형을 개선해서, 태양으로 부터 가까운 순서대로 각 행성의 순서를 원시값으로 넣었다.

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

 

In the example above, Planet.mercury has an explicit raw value of 1, Planet.venus has an implicit raw value of 2, and so on.

When strings are used for raw values, the implicit value for each case is the text of that case’s name.

The enumeration below is a refinement of the earlier CompassPoint enumeration, with string raw values to represent each direction’s name:

위의 예에서, Planet.mercury 는 명시적으로 원시값 1을 가지고 있다.

Planet.venus 는 암시적으로 원시값 2를 가지고 있다. 기타 등등 (갑자기 옛날영화 왕과나 의 율브리너가 생각나네. ㅋ)

문자열을 원시값으로 사용할때, 각 case에 대한 암시적 값은 case 이름이다.

아래의 열거혀은 CompassPoint를 개선해서, 각 방향의 이름을 원시값으로 갖도록 하였다.

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

 

In the example above, CompassPoint.south has an implicit raw value of "south", and so on.

You access the raw value of an enumeration case with its rawValue property:

위의 예에서, CompassPoint.south 는 암시적으로 원시값 "south"를 가진다. 기타등등.

열거형 case의 원시값은 rawValue 속성으로 접근할수 있다.

let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3

let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"

 

Initializing from a Raw Value

If you define an enumeration with a raw-value type, the enumeration automatically receives an initializer that takes a value of the raw value’s type (as a parameter called rawValue) and returns either an enumeration case or nil. You can use this initializer to try to create a new instance of the enumeration.

This example identifies Uranus from its raw value of 7:

원시값을 가지는 열거형을 정의하면, 그 열거형은 자동으로 원시값의 타입을 가지는 (매개변수 이름은 rawValue) 초기화를 받고,

열거형 또는 nil 을 리턴한다.

너는 열거형의 인스턴스 생성할때 이 초기화를 사용할수 있다.

이 예에서 원시값 7은 천왕성을 가르킨다.

let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus

Not all possible Int values will find a matching planet, however. Because of this, the raw value initializer always returns an optional enumeration case. In the example above, possiblePlanet is of type Planet?, or “optional Planet.”

모든 Int 값이 일치하는 행성을 찾는건 아니다.

그렇기 때문에 원시값 초기화는 항상 optional 열거형을 리턴한다.

위의 예에서 possiblePlanet 의 타입은 Planet? 또는 optional Planet 이다.

 

NOTE

The raw value initializer is a failable initializer, because not every raw value will return an enumeration case. For more information, see Failable Initializers.

원시값 초기화는 실패할수 있다. 왜냐면 모든 원시값이 열거형을 리턴하지는 않으니까.

 

If you try to find a planet with a position of 11, the optional Planet value returned by the raw value initializer will be nil:

만약 11번째의 행성을 찾으려 하면, 원시값 초기화가 리턴하는 optional Planet 값은 nil 이 된다.

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"

 

This example uses optional binding to try to access a planet with a raw value of 11. The statement if let somePlanet = Planet(rawValue: 11) creates an optional Planet, and sets somePlanet to the value of that optional Planet if it can be retrieved. In this case, it isn’t possible to retrieve a planet with a position of 11, and so the else branch is executed instead.

이 예제는 optional 바인딩을 사용해서 원시값이 11인 행성에 접근하려고 한다.

if let somePlanet = Planet... 문장은 optional Planet 을 만들고, 가능하다면 somePlanet 에 optional Planet 값을 저장한다.

이경우에는 11번째의 행성을 가져오는게 불가능해서, else 문이 실행된다.

Recursive Enumerations

A recursive enumeration is an enumeration that has another instance of the enumeration as the associated value for one or more of the enumeration cases. You indicate that an enumeration case is recursive by writing indirect before it, which tells the compiler to insert the necessary layer of indirection.

For example, here is an enumeration that stores simple arithmetic expressions:

재귀열거형은 하나이상의 case에 연관된값으로 다른 열거형의 인스턴스를 가지는 열거형이다.

case의 앞에 indirect 를 적어서 재귀된다는걸 표시해서, 컴파일러가 간접적으로 필요한 사항을 넣을수 있도록 한다.

예를들어, 이 열거형은 단순한 사칙연산 표현을 저장한다.

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

 

You can also write indirect before the beginning of the enumeration to enable indirection for all of the enumeration’s cases that have an associated value:

또한 열거형 맨 앞에 indirect 를 적어서 연관된 값을 가지는 모든 case들이 간접적이 되도록 만들수 있다.

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

 

This enumeration can store three kinds of arithmetic expressions: a plain number, the addition of two expressions, and the multiplication of two expressions. The addition and multiplication cases have associated values that are also arithmetic expressions—these associated values make it possible to nest expressions. For example, the expression (5 + 4) * 2 has a number on the right-hand side of the multiplication and another expression on the left-hand side of the multiplication. Because the data is nested, the enumeration used to store the data also needs to support nesting—this means the enumeration needs to be recursive. The code below shows the ArithmeticExpression recursive enumeration being created for (5 + 4) * 2:

이 열거형은 3가지 종류의 산술표현을 저장할수 있다. 일반숫자, 표현식 2개를 더하기, 표현식 2개를 곱하기.

addition과 multiplication case는 산술표현인 연관된 값을 가지고 있다.

이 연관된 값들은 중첩된 표현식을 가능하게 한다.

예를들어 expression (5+4)*2 는 곱셈의 오른쪽에 숫자가 있고, 곱셈의 왼쪽에는 다른 표현식이 있다.

데이타가 중첩되었기 때문에, 데이타를 저장하는 열거형 또한 중첩을 지원해야 한다.

이 말은 열거형이 재귀를 필요로 한다는 뜻이다.

아래 코드는 열거형을 재귀하는 ArithmeticExpression 이 (5+4)*2를 생성하는걸 보여준다.

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

 

A recursive function is a straightforward way to work with data that has a recursive structure. For example, here’s a function that evaluates an arithmetic expression:

재귀함수는 재귀적 구조를 가진 데이타를 처리하는 간단한 방법이다.

아래는 산술표현식을 평가하는 함수의 예이다.

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"

 

This function evaluates a plain number by simply returning the associated value. It evaluates an addition or multiplication by evaluating the expression on the left-hand side, evaluating the expression on the right-hand side, and then adding them or multiplying them.

반응형

'iOS 초보' 카테고리의 다른 글

[swift5.1번역] 10.Properties  (0) 2019.08.14
[swift5.1번역] 9.Structures and Classes  (0) 2019.08.07
[swift5.1번역] 7.Closures  (0) 2019.07.25
[swift5.1번역] 6.Functions  (0) 2019.07.09
[swift5.1번역] 5.Control Flow  (0) 2019.07.03
Posted by 돌비
,