Initial commit
This commit is contained in:
865
skills/programming-swift/LanguageGuide/Enumerations.md
Normal file
865
skills/programming-swift/LanguageGuide/Enumerations.md
Normal file
@@ -0,0 +1,865 @@
|
||||
# Enumerations
|
||||
|
||||
Model custom types that define a list of possible values.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
<doc:Properties>, <doc:Methods>, <doc:Initialization>,
|
||||
<doc:Extensions>, and <doc:Protocols>.
|
||||
|
||||
<!--
|
||||
TODO: this chapter should probably mention that enums without associated values
|
||||
are hashable and equatable by default (and what that means in practice)
|
||||
-->
|
||||
|
||||
## Enumeration Syntax
|
||||
|
||||
You introduce enumerations with the `enum` keyword
|
||||
and place their entire definition within a pair of braces:
|
||||
|
||||
```swift
|
||||
enum SomeEnumeration {
|
||||
// enumeration definition goes here
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> enum SomeEnumeration {
|
||||
// enumeration definition goes here
|
||||
}
|
||||
```
|
||||
-->
|
||||
|
||||
Here's an example for the four main points of a compass:
|
||||
|
||||
```swift
|
||||
enum CompassPoint {
|
||||
case north
|
||||
case south
|
||||
case east
|
||||
case west
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> 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`.
|
||||
|
||||
Multiple cases can appear on a single line, separated by commas:
|
||||
|
||||
```swift
|
||||
enum Planet {
|
||||
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> 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:
|
||||
|
||||
```swift
|
||||
var directionToHead = CompassPoint.west
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> 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:
|
||||
|
||||
```swift
|
||||
directionToHead = .east
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> 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:
|
||||
|
||||
```swift
|
||||
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".
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> 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")
|
||||
}
|
||||
<- 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 <doc:ControlFlow>,
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
```swift
|
||||
let somePlanet = Planet.earth
|
||||
switch somePlanet {
|
||||
case .earth:
|
||||
print("Mostly harmless")
|
||||
default:
|
||||
print("Not a safe place for humans")
|
||||
}
|
||||
// Prints "Mostly harmless".
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> let somePlanet = Planet.earth
|
||||
-> switch somePlanet {
|
||||
case .earth:
|
||||
print("Mostly harmless")
|
||||
default:
|
||||
print("Not a safe place for humans")
|
||||
}
|
||||
<- 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:
|
||||
|
||||
```swift
|
||||
enum Beverage: CaseIterable {
|
||||
case coffee, tea, juice
|
||||
}
|
||||
let numberOfChoices = Beverage.allCases.count
|
||||
print("\(numberOfChoices) beverages available")
|
||||
// Prints "3 beverages available".
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> enum Beverage: CaseIterable {
|
||||
case coffee, tea, juice
|
||||
}
|
||||
-> let numberOfChoices = Beverage.allCases.count
|
||||
-> print("\(numberOfChoices) beverages available")
|
||||
<- 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`-`in` loop to iterate over all the cases.
|
||||
|
||||
```swift
|
||||
for beverage in Beverage.allCases {
|
||||
print(beverage)
|
||||
}
|
||||
// coffee
|
||||
// tea
|
||||
// juice
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> for beverage in Beverage.allCases {
|
||||
print(beverage)
|
||||
}
|
||||
<< coffee
|
||||
<< tea
|
||||
<< juice
|
||||
// coffee
|
||||
// tea
|
||||
// juice
|
||||
```
|
||||
-->
|
||||
|
||||
The syntax used in the examples above
|
||||
marks the enumeration as conforming to the
|
||||
[`CaseIterable`](https://developer.apple.com/documentation/swift/caseiterable) protocol.
|
||||
For information about protocols, see <doc: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.
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||
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)
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> 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`.”
|
||||
|
||||
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're equal to `Barcode.upc` or `Barcode.qrCode`.
|
||||
|
||||
You can then create new barcodes using either type:
|
||||
|
||||
```swift
|
||||
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> 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:
|
||||
|
||||
```swift
|
||||
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> 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.
|
||||
|
||||
You can check the different barcode types using a switch statement,
|
||||
similar to the example in
|
||||
<doc:Enumerations#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:
|
||||
|
||||
```swift
|
||||
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."
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> 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).")
|
||||
}
|
||||
<- 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 `let` or `var` annotation before the case name, for brevity:
|
||||
|
||||
```swift
|
||||
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."
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `enums`
|
||||
|
||||
```swifttest
|
||||
-> switch productBarcode {
|
||||
case let .upc(numberSystem, manufacturer, product, check):
|
||||
print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
|
||||
case let .qrCode(productCode):
|
||||
print("QR code: \(productCode).")
|
||||
}
|
||||
<- QR code: ABCDEFGHIJKLMNOP.
|
||||
```
|
||||
-->
|
||||
|
||||
When you're matching just one case of an enumeration ---
|
||||
for example,
|
||||
to extract its associated value ---
|
||||
you can use an `if`-`case` statement
|
||||
instead of writing a full switch statement.
|
||||
Here's what it looks like:
|
||||
|
||||
```swift
|
||||
if case .qrCode(let productCode) = productBarcode {
|
||||
print("QR code: \(productCode).")
|
||||
}
|
||||
```
|
||||
|
||||
Just like in the switch statement earlier,
|
||||
the `productBarcode` variable is matched against
|
||||
the pattern `.qrCode(let productCode)` here.
|
||||
And as in the switch case,
|
||||
writing `let` extracts the associated value as a constant.
|
||||
For more information about `if`-`case` statements,
|
||||
see <doc:ControlFlow#Patterns>.
|
||||
|
||||
## Raw Values
|
||||
|
||||
The barcode example in <doc:Enumerations#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:
|
||||
|
||||
```swift
|
||||
enum ASCIIControlCharacter: Character {
|
||||
case tab = "\t"
|
||||
case lineFeed = "\n"
|
||||
case carriageReturn = "\r"
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `rawValues`
|
||||
|
||||
```swifttest
|
||||
-> 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 <doc:StringsAndCharacters>.
|
||||
|
||||
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.
|
||||
|
||||
Although you can use both raw values and associated values
|
||||
to give an enumeration an additional value,
|
||||
it's important to understand the difference between them.
|
||||
You pick the raw value for an enumeration case
|
||||
when you define that enumeration case in your code,
|
||||
such as the three ASCII codes above.
|
||||
The raw value for a particular enumeration case is always the same.
|
||||
In contrast,
|
||||
you pick associated values when you create a new constant or variable
|
||||
using one of the enumeration's cases,
|
||||
and you can pick a different value each time you do so.
|
||||
|
||||
### 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:
|
||||
|
||||
```swift
|
||||
enum Planet: Int {
|
||||
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `rawValues`
|
||||
|
||||
```swifttest
|
||||
-> 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:
|
||||
|
||||
```swift
|
||||
enum CompassPoint: String {
|
||||
case north, south, east, west
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `rawValues`
|
||||
|
||||
```swifttest
|
||||
-> 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:
|
||||
|
||||
```swift
|
||||
let earthsOrder = Planet.earth.rawValue
|
||||
// earthsOrder is 3
|
||||
|
||||
let sunsetDirection = CompassPoint.west.rawValue
|
||||
// sunsetDirection is "west"
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `rawValues`
|
||||
|
||||
```swifttest
|
||||
-> let earthsOrder = Planet.earth.rawValue
|
||||
/> earthsOrder is \(earthsOrder)
|
||||
</ earthsOrder is 3
|
||||
|
||||
-> let sunsetDirection = CompassPoint.west.rawValue
|
||||
/> sunsetDirection is \"\(sunsetDirection)\"
|
||||
</ 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`:
|
||||
|
||||
```swift
|
||||
let possiblePlanet = Planet(rawValue: 7)
|
||||
// possiblePlanet is of type Planet? and equals Planet.uranus
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `rawValues`
|
||||
|
||||
```swifttest
|
||||
-> let possiblePlanet = Planet(rawValue: 7)
|
||||
>> print(type(of: possiblePlanet))
|
||||
<< Optional<Planet>
|
||||
>> assert(possiblePlanet == .uranus)
|
||||
// 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`.”
|
||||
|
||||
> Note: The raw value initializer is a failable initializer,
|
||||
> because not every raw value will return an enumeration case.
|
||||
> For more information, see <doc:Declarations#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`:
|
||||
|
||||
```swift
|
||||
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".
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `rawValues`
|
||||
|
||||
```swifttest
|
||||
-> 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)")
|
||||
}
|
||||
<- 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.
|
||||
|
||||
<!--
|
||||
TODO: Switch around the order of this chapter so that all of the non-union stuff
|
||||
is together, and the union bits (aka Associated Values) come last.
|
||||
-->
|
||||
|
||||
## 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:
|
||||
|
||||
```swift
|
||||
enum ArithmeticExpression {
|
||||
case number(Int)
|
||||
indirect case addition(ArithmeticExpression, ArithmeticExpression)
|
||||
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `recursive-enum-intro`
|
||||
|
||||
```swifttest
|
||||
-> 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:
|
||||
|
||||
```swift
|
||||
indirect enum ArithmeticExpression {
|
||||
case number(Int)
|
||||
case addition(ArithmeticExpression, ArithmeticExpression)
|
||||
case multiplication(ArithmeticExpression, ArithmeticExpression)
|
||||
}
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `recursive-enum`
|
||||
|
||||
```swifttest
|
||||
-> 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`:
|
||||
|
||||
```swift
|
||||
let five = ArithmeticExpression.number(5)
|
||||
let four = ArithmeticExpression.number(4)
|
||||
let sum = ArithmeticExpression.addition(five, four)
|
||||
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `recursive-enum`
|
||||
|
||||
```swifttest
|
||||
-> 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:
|
||||
|
||||
```swift
|
||||
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".
|
||||
```
|
||||
|
||||
<!--
|
||||
- test: `recursive-enum`
|
||||
|
||||
```swifttest
|
||||
-> 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))
|
||||
<- 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.
|
||||
|
||||
<!--
|
||||
This source file is part of the Swift.org open source project
|
||||
|
||||
Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
|
||||
Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
|
||||
See https://swift.org/LICENSE.txt for license information
|
||||
See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
-->
|
||||
Reference in New Issue
Block a user