Wednesday, June 28, 2017

Enumeration - Part 2 (Swift)

7:50 AM Posted by CHANDAN MAKHIJA 1 comment
Associated Value Enumerations
Associated values, allow us to combine a set of enumeration cases and yet associate different sets of values of with each enumeration case. This includes any number of values of any type we like as well as potentially having no associated values at all. In Swift we can’t use both raw values and associated values within the same enumeration at the same time.
Let’s have a look at an example.
Here I’ve defined an enumeration to represent colours in two different colour spaces):
enum ColorSpace {
    case rgba(red: UInt8, green: UInt8, blue: UInt8, alpha: Float)
    case cmyk(cyan: Float, magenta: Float, yellow: Float, black: Float)
}

 There are a few things to take note of:
First, there is no raw value type after the enumeration as we saw with raw values (i.e. no colon followed by a type name after the enumeration name). This is because with associated values, each enumeration case can have a different set of associated values and it therefore doesn’t really make sense to specify a single type.
Secondly, notice that the .rgba and .cmyk cases have a different sets of values associated with them with each set of associated values being defined as a comma separated list of label / type pairs between a pair of parentheses. The key point is that each enumeration case only has the associated values that it needs.
Another thing of note is that it is not actually a requirement that we supply labels for each of the associated values. We could just as easily have written the enumeration above as:
enum ColorSpace {
    case rgba(UInt8, UInt8, UInt8, Float)
    case cmyk(Float, Float, Float, Float)
}

Assigning Enumerations Values with Associated Values

To create an enumeration value with associated values, we must supply values for each of the associated values of the enumeration at the point we assign the enumeration value:
var color1 = Color.rbga(red: 100, green: 100, blue: 100, alpha:1.0)
var color2 = Color.cmyk(cyan: 0.5, magenta: 0.5, yellow: 0.5, black: 0.5)

Enumerations and Pattern Matching

For simple enumerations and enumerations with raw values, we can use a simple enumeration case pattern to match individual enumeration cases:
enum DayOfWeek {
    case Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}

let day = DayOfWeek.Monday

switch day {
    case .Monday: print("The Moon's day")
    case .Tuesday: print("The Norse god Tyr")
    case .Wednesday: print("The Norse god Odin")
    case .Thursday: print("The Norse god Thor")
    case .Friday: print("The Norse god Frigg")
    case .Saturday: print("Saturn's Day")
    case .Sunday: print("The Sun's Day")
}
// prints "The Moon's day"

When it comes to enumeration cases with associated values, we also have the option of using the value-binding pattern to match and extract enumeration cases and their associated values:
enum Day {
    case Monday(units: Int)
    case Tuesday(units: Int)
    case Wednesday(units: Int)
    case Thursday(units: Int)
    case Friday(units: Int)
    case Saturday(units: Int)
    case Sunday(units: Int)
}

let sales = Day.Monday(units: 42)

switch sales {
case let .Monday(units):
    print("Sold \(units) on Monday")
case let .Tuesday(units):
    print("Sold \(units) on Tuesday")
// ...
default:
    print("Not sure about the rest of the week!")
}

Enumeration Equality

Enumerations checking for equality is relatively straight forward:
enum Planet {
    case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto
}

var planet = Planet.Mercury
if planet == .Earth {
    print("The Blue Marble")
}

This also works for enumerations with raw values:
enum Planet : Int {
    case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto
}

var planet = Saturn
if planet == .Saturn {
    print("The Ringed Planet")
}

Boolean Comparison of Associated Values Enumerations

This is little difficult because Swift doesn’t know how to compare the different associated values for each enumeration case to determine whether two cases are equal or not. To fix this, we have to give Swift a bit of help. This means implementing the equality operator (==) for the enumeration type ourselves.
For example:
enum UserAction {
    case Start
    case Pause
    case Stop
    case Restart(delay: Int)
}

func ==(lhs: UserAction, rhs: UserAction) -> Bool {
    switch (lhs, rhs) {
    case let (.Restart(delay1), .Restart(delay2)):
        return delay1 == delay2
    case (.Start, .Start), (.Pause, .Pause), (.Stop, .Stop):
        return true
    default:
        return false
    }
}

UserAction.Start == UserAction.Start // true
UserAction.Start == UserAction.Restart(delay:10) // false
UserAction.Restart(delay: 10) == UserAction.Restart(delay:12) // false
UserAction.Restart(delay: 10) == UserAction.Restart(delay: 10) // true


Recursive Enumerations
It’s probably easier to explain recursive enumerations with an example. Imagine we wanted to represent a binary tree within our code.
We could model the binary tree using an enumeration as follows:
enum BinaryTree<T> {
    case leaf(T)
    case node(leftChild: BinaryTree, rightChild: BinaryTree?)
}

The key point here is that the second case of the enumeration has up to two associated values which themselves are also of the same type as enumeration type that is being defined (BinaryTree<T>).
The other thing with this example is that, as written, it doesn’t actually compile.
If we put this into a playground, Xcode displays the following error
Recursive enum ‘BinaryTree<T>’ is not marked as ‘indirect’.
Recursive definitions like the one written above aren’t actually allowed.Instead, we have to tell Swift to modify the way that it stores the associated values of the enumeration by including the indirect keyword.
enum BinaryTree<T> {
    case leaf(T)
    indirect case node(left: BinaryTree, right: BinaryTree?)
}
This modifies the storage of just those cases only.
indirect enum BinaryTree<T> {
    case leaf(T)
    case node(left: BinaryTree, right: BinaryTree?)

This will modify the storage of all cases with associated values


Nested Enumerations

Nested enumerations are enumerations that are defined within the body of another enumeration. The main reason for defining nested enumerations is so we can group together enumerations that are related, for example where one enumeration is used as the associated value of one or more cases in another enumeration.
enum TyreType {
    enum TyreColor {
        case purple // UltraSoft
        case red // SuperSoft      
        case yellow // Soft
        case white // Medium
        case orange // Hard
        case green // Intermediate
        case blue // FullWet
    }
    
    case ultraSoft(color: TyreColor)
    case superSoft(color: TyreColor)
    case soft(color: TyreColor)
    case medium(color: TyreColor)
    case hard(color: TyreColor)
    case intermediate(color: TyreColor, litresClearedPerSecond: UInt8)
    case fullWet(color: TyreColor, litresClearedPerSecond: UInt8)
}

let qualificationTyre = TyreType.ultraSoft(color: .purple)
let raceTyre = TyreType.intermediate(color:.green, litresClearedPerSecond: 25)

Contained Enumerations

We can contain enumerations within other structured types such as classes or structs.
For example
struct F1RacingCar {
    enum RaceTeam {
        case mercedes
        case ferrari
        case redBullRacing
        case williams
        case toroRosso
        case mcLaren
        case forceIndia
        case renault
        case sauber
        case haasF1
        case manorRacing
    }

    enum TyreType {
        case ultraSoft
        case superSoft
        case soft
        case medium
        case hard
        case intermediate
        case fullWet
    }

    let team: RaceTeam
    let tyre: TyreType
}

let racingCar = F1RacingCar(team: .mercedes, tyre: .superSoft)

1 comment:

  1. I would highly recommend Mr, Benjamin services to any person in need financial help and they will keep you on top of high directories for any further needs. Once again I commend yourself and your staff for extraordinary service and customer service, as this is a great asset to your company and a pleasant experience to customers such as myself. Wishing you all the best for the future.Mr, Benjamin is best way to get an easy loan,here is there email.. / lfdsloans@outlook.com  Or talk to Mr Benjamin On WhatsApp Via_+1-989-394-3740 Thank You for helping me with loan once again in my sincerely heart I'm forever grateful.

    ReplyDelete