The Swift Programming Language Enumerations Experiment - xcode

I'm making my way through The Swift Programming Language book, but I'm stuck on an experiment.
I'm given this code:
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> String {
switch self {
case .Ace:
return "Ace"
case .Jack:
return "Jack"
case .Queen:
return "Queen"
case .King:
return "King"
default:
return String(self.toRaw())
}
}
}
For the experiment, I have to "Write a function that compares two Rank values by comparing their raw values.
I had a go:
func rankCompare(first: String, second: String) -> String {
let firstRank = Rank.first
}
But I ended up with errors because I don't know how to pass Enum values.
Can someone help?

Enum values can be passed just like other types. The following function is part of the Rank enum and compares one Rank to another.
func compareToOther(other:Rank) -> Bool { // other is of type Rank
return self.toRaw() == other.toRaw()
}
Here is a screenshot of the quick implementation and usage.

You can pass enums by just passing the enum name:
// someRank is a Rank enum value
func myFunction (someRank: Rank) -> () {
}
And then you can just call it:
myFunction(Rank.Ace)

I am also a beginner, but this is how I worked throughout the experiment. First I added this;
func compareTwoCards(card1: Rank, card2: Rank) -> String {
if card1.toRaw() == card2.toRaw() {
return "Cards are equal"
} else {
if card1.toRaw() > card2.toRaw() {
return "Card1 is greater"
} else {
return "Card2 is greater"
}
} }
Then I created two Rank objects
let ace = Rank.Ace
let queen = Rank.Queen
Finally, I called it three different ways to test it;
compareTwoCardsTake2(ace, queen)
compareTwoCardsTake2(queen, ace)
compareTwoCardsTake2(ace, ace)
Can some one with more experience please reply if there is a better/more elegant way of performing the compare?

I solved it like this:
func rankCompare(first: Rank, second: Rank) -> String {
if(first.rawValue > second.rawValue) {
return "\(first.simpleDescription()) beats \(second.simpleDescription())."
}
else if second.rawValue > first.rawValue {
return "\(second.simpleDescription()) beats \(first.simpleDescription())."
}
else {
return "\(first.simpleDescription()) equals \(second.simpleDescription())."
}
}
let king = Rank.King
let queen = Rank.Queen
let seven = Rank.Seven
rankCompare(king, queen)
rankCompare(seven, king)
rankCompare(queen, queen)
Use .rawValue for doing the comparisons and .simpleDescription() for writing out your answer.

This code can be used to determine if two enumeration values are equal or not. .toRaw() is obsolete, so .rawValue must be used to obtain the raw value for comparison. An edited version of this function (to make a full comparison with type string information, not just "true" or "false") should be used to complete the exercise. Hint For Editing: this function is of type Bool.
func compareRanks(rankA: Rank, rankB: Rank) -> Bool {
return rankA.rawValue == rankB.rawValue
}
To see the code and contributors that made this answer possible, please see the question: Explanation of The Swift Programming Language Enumerations Experiment

Related

Why do we use the Option enum?

I don't get what the Option enum is for. I read that Rust doesn't have null values. The Option enum is defined like this:
enum Option<T> {
Some(T),
None,
}
I read its implementation and I came across this example:
fn main() {
fn divide(numerator: f64, denominator: f64) -> Option<f64> {
if denominator == 0.0 {
None
} else {
Some(numerator / denominator)
}
}
// The return value of the function is an option
let result = divide(2.0, 3.0);
// Pattern match to retrieve the value
match result {
// The division was valid
Some(x) => println!("Result: {}", x),
// The division was invalid
None => println!("Cannot divide by 0"),
}
}
When they could also do it like this:
fn main() {
fn divide(numerator: f64, denominator: f64) -> String {
if denominator == 0.0 {
format!("Can't divide")
} else {
let x = numerator / denominator;
format!("{}", x)
}
}
let result = divide(2.0, 3.0);
println!("{}", result);
}
Both programs output:
0.6666666666666666
Maybe the above example is not a very good example of Option, but the following example shows Option at its very best:
fn main() {
let name = String::from("naufil");
println!(
"Character at index 6: {}",
match name.chars().nth(6) {
Some(c) => c.to_string(),
None => "No character at index 6!".to_string(),
}
)
}
When we are not sure whether there is a character at 6th element and you don't want your program to crash, Option comes to the rescue. Here is another example from The Rust Programming Language:
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
Listing 6-5: A function that uses a match expression on
an Option<i32>
Let’s examine the first execution of plus_one in more detail. When we call
plus_one(five), the variable x in the body of plus_one will have the
value Some(5). We then compare that against each match arm.
None => None,
The Some(5) value doesn’t match the pattern None, so we continue to the
next arm.
Some(i) => Some(i + 1),
Does Some(5) match Some(i)? Why yes it does! We have the same variant. The
i binds to the value contained in Some, so i takes the value 5. The
code in the match arm is then executed, so we add 1 to the value of i and
create a new Some value with our total 6 inside.
Now let’s consider the second call of plus_one in Listing 6-5, where x is
None. We enter the match and compare to the first arm.
None => None,
It matches! There’s no value to add to, so the program stops and returns the
None value on the right side of =>. Because the first arm matched, no other
arms are compared.
Combining match and enums is useful in many situations. You’ll see this
pattern a lot in Rust code: match against an enum, bind a variable to the
data inside, and then execute code based on it. It’s a bit tricky at first, but
once you get used to it, you’ll wish you had it in all languages. It’s
consistently a user favorite.
The reason the Option enum is used for the same reason the Result enum is used. It allows the programmer to see the breadth of returning values they might receive, but without having to dig through code you don't remember all the details about, or have never seen.
Option isn't a special value, it's just an enum, like Result. You could also use something like:
enum Division_Result {
Successful(f64),
DividedByZero,
}
fn divide(numerator: f64, denominator: f64) -> Division_Result {
if denominator == 0.0 {
Division_Result::DividedByZero
} else {
Division_Result::Successful(numerator / denominator)
}
}
It just so happens that Optional values are some of the most common types of values that you have to deal with in a program. They're baked the Optional enum into standard because otherwise you would have to deal with everyone coming up with their own enum for the simple concept of an Optional value.
Returning an enum is an improvement over returning unwrapped magic values because it is more explicit to the programmer that the return value might diverge from what they wanted from the function.

Sharing a common value in all enum values

I have the following code where every variant of the enum Message has a Term value associated with it:
type Term = usize;
pub enum Message {
AppendRequest(Term),
AppendResponse(Term),
VoteRequest(Term),
VoteResponse(Term),
}
impl Message {
pub fn term(&self) -> Term {
match *self {
Message::AppendRequest(term) => term,
Message::AppendResponse(term) => term,
Message::VoteRequest(term) => term,
Message::VoteResponse(term) =>term,
}
}
}
I want to, given a Message be able to get its term without having to deconstruct the actual Message value I have. The best I could come up with was creating a public function that unpacked the value for me, but this feels clunky. If I ever add a new enum value, I'm going to have to remember to update match statement in the term function.
Is there a more succinct/ergonomic way to express the code above? Is there some way to say "hey, every value for this enum will have also have a Term value associated with it.
Is there some way to say "hey, every value for this enum will have also have a Term value associated with it.
No. This is usually handled by splitting the enum into two parts, with a struct containing all the common parts:
pub struct Message {
term: Term,
kind: MessageKind,
}
pub enum MessageKind {
AppendRequest,
AppendResponse,
VoteRequest,
VoteResponse,
}
One option is to implement the Deref (and/or DerefMut) trait to convert to the common part.
You still have to update that implementation each time you add to the Enum, but there is less boilerplate at the point of use.
E.g., an example below, note that main accesses the field number on the Enum.
use std::ops::Deref;
use std::string::String;
enum JudgedNumber {
GoodNumber(Number),
BadNumber(Number, String),
}
struct Number { number: i32 }
fn main() {
let nice = JudgedNumber::GoodNumber(Number{number: 42});
let naughty = JudgedNumber::BadNumber(
Number{number: 666}, "Damn you to hell".to_string());
println!("j1 = {}", j1.number);
println!("j2 = {}", j2.number);
}
impl Deref for JudgedNumber {
type Target = Number;
fn deref(&self) -> &Number {
match self {
JudgedNumber::GoodNumber(n) => n,
JudgedNumber::BadNumber(n, _) => n,
}
}
}
I learnt this from https://github.com/rust-embedded/svd/blob/master/src/svd/cluster.rs

binary operator cannot be applied to operands

Recently, i am reading "functional programming in swift". In the book, the author does some extension of Int to meet a protocol Smaller. In order to get a thorough understanding of the author's idea, i copy the code to my own playground, but it reports error.
protocol Smaller {
static func smaller() -> Self?
}
extension Int: Smaller {
static func smaller() -> Int? {
//reporting error: Binary operator "==" cann't be applied to type of Int.type and Int
return self == 0 ? nil : self / 2
}
}
it seems that self == 0 is not allowed in the extension. Does anybody have an idea of the reason.
I don't think you wanted to use a static function as you need a instantiated integer to work on and check if it is smaller.
So there are 2 approaches:
Remove the static from the function and then call it normally:
let aInt = 4
aInt.smaller() //will be 2
or you change the signature of the static function to accept the instance as an argument
`
protocol Smaller {
static func smaller(selfToMakeSmall: Self) -> Self?
}
extension Int: Smaller {
static func smaller(selfToMakeSmall: Int) -> Int? {
//reporting error: Binary operator "==" cann't be applied to type of Int.type and Int
return selfToMakeSmall == 0 ? nil : selfToMakeSmall / 2
}
}
let theInt = 4
Int.smaller(theInt)
`
but I think this could be also improved with Generics

How to choose a random enumeration value

I am trying to randomly choose an enum value:
enum GeometryClassification {
case Circle
case Square
case Triangle
case GeometryClassificationMax
}
and the random selection:
let shapeGeometry = ( arc4random() % GeometryClassification.GeometryClassificationMax ) as GeometryClassification
but it fails.
I get errors like:
'GeometryClassification' is not convertible to 'UInt32'
How do I solve this?
In Swift there is actually a protocol for enums called CaseIterable that, if you add it to your enum, you can just reference all of the cases as a collection with .allCases as so:
enum GeometryClassification: CaseIterable {
case Circle
case Square
case Triangle
}
and then you can .allCases and then .randomElement() to get a random one
let randomGeometry = GeometryClassification.allCases.randomElement()!
The force unwrapping is required because there is a possibility of an enum having no cases and thus randomElement() would return nil.
Swift has gained new features since this answer was written that provide a much better solution — see "How to choose a random enumeration value" instead.
I'm not crazy about your last case there -- it seems like you're including .GeometryClassificationMax solely to enable random selection. You'll need to account for that extra case everywhere you use a switch statement, and it has no semantic value. Instead, a static method on the enum could determine the maximum value and return a random case, and would be much more understandable and maintainable.
enum GeometryClassification: UInt32 {
case Circle
case Square
case Triangle
private static let _count: GeometryClassification.RawValue = {
// find the maximum enum value
var maxValue: UInt32 = 0
while let _ = GeometryClassification(rawValue: maxValue) {
maxValue += 1
}
return maxValue
}()
static func randomGeometry() -> GeometryClassification {
// pick and return a new value
let rand = arc4random_uniform(_count)
return GeometryClassification(rawValue: rand)!
}
}
And you can now exhaust the enum in a switch statement:
switch GeometryClassification.randomGeometry() {
case .Circle:
println("Circle")
case .Square:
println("Square")
case .Triangle:
println("Triangle")
}
Since you're inside the enum class, having the random() method reference the highest value explicitly would eliminate having to count them every time:
enum GeometryClassification: UInt32 {
case Circle
case Square
case Triangle
static func random() -> GeometryClassification {
// Update as new enumerations are added
let maxValue = Triangle.rawValue
let rand = arc4random_uniform(maxValue+1)
return GeometryClassification(rawValue: rand)!
}
}
For Swift 5 there is "RandomNumberGenerator":
enum Weekday: CaseIterable {
case sunday, monday, tuesday, wednesday, thursday, friday, saturday
static func random<G: RandomNumberGenerator>(using generator: inout G) -> Weekday {
return Weekday.allCases.randomElement(using: &generator)!
}
static func random() -> Weekday {
var g = SystemRandomNumberGenerator()
return Weekday.random(using: &g)
}
}
You need to assign a raw type to your enum. If you use an integer type, then the enumeration case values will be auto-generated starting at 0:
enum GeometryClassification: UInt32 {
case Circle
case Square
case Triangle
case GeometryClassificationMax
}
Per Enumerations:
"Unlike C and Objective-C, Swift enumeration members are not assigned a default integer value when they are created."
Specifying the integer type lets it know to generate the values in the usual way.
Then you can generate the random value like this:
let randomEnum: GeometryClassification = GeometryClassification.fromRaw(arc4random_uniform(GeometryClassification.GeometryClassificationMax.toRaw()))!
This is a horribly ugly call, and all those fromRaw and toRaw calls are fairly inelegant, so I would really recommend generating a random UInt32 that is in the range you want first, then creating a GeometryClassification from that value:
GeometryClassification.fromRaw(someRandomUInt32)
You can put all the values into array and generate random,
extension GeometryClassification {
static func random() -> GeometryClassification {
let all: [GeometryClassification] = [.Circle,
.Square,
.Triangle,
.GeometryClassificationMax]
let randomIndex = Int(arc4random()) % all.count
return all[randomIndex]
}
}
The easiest thing to do is to create a global extension:
extension CaseIterable {
static func randomElement() -> AllCases.Element {
guard Self.allCases.count > 0 else {
fatalError("There must be at least one case in the enum")
}
return Self.allCases.randomElement()!
}
}
This way any enum which conforms to CaseIterable has the function automatically
Here's my Swift 1.2 take:
enum GeometryClassification : Int {
case Circle = 0
case Square = 1
case Triangle = 2
static func random() -> GeometryClassification {
let min = MutationType.Circle.rawValue
let max = MutationType.Triangle.rawValue
let rand = Int.random(min: min, max: max) // Uses ExSwift!
return self(rawValue: rand)!
}
}
I wrote a global extension using Andy's answer. Enjoy :)
extension CaseIterable {
static func random<G: RandomNumberGenerator>(using generator: inout G) -> Self.AllCases.Element {
return Self.allCases.randomElement(using: &generator)!
}
static func random() -> Self.AllCases.Element {
var g = SystemRandomNumberGenerator()
return Self.random(using: &g)
}
}
Just extend your enumeration to conform CaseIterable protocol and use like:
let state = YourEnum.random()

Testing for enum value fails if one has associated value?

I'm testing this in the Playground and I'm not sure how to do this. With a normal enum that doesn't have associated values, everything is fine.
enum CompassPoint {
case North
case South
case East
case West
}
var direction = CompassPoint.East
if direction != .West {
println("Go West!")
}
However, if one of my enums has an associated value, the direction test fails with this error: could not find member 'West'
enum CompassPoint {
case North(Int)
case South
case East
case West
}
var direction = CompassPoint.East
if direction != .West {
println("Go West!")
}
What can I do to allow for this test?
Enumerations are automatically Equatable when they have a raw value that's Equatable. In your first case, the raw value is assumed to be Int, but it would work if you'd given it another specific type like UInt32 or even String.
Once you add an associated value, however, this automatic conformance with Equatable doesn't happen any more, since you can declare this:
let littleNorth = CompassPoint.North(2)
let bigNorth = CompassPoint.North(99999)
Are those equal? How should Swift know? You have to tell it, by declaring the enum as Equatable and then implementing the == operator:
enum CompassPoint : Equatable {
case North(Int)
case South
case East
case West
}
public func ==(lhs:CompassPoint, rhs:CompassPoint) -> Bool {
switch (lhs, rhs) {
case (.North(let lhsNum), .North(let rhsNum)):
return lhsNum == rhsNum
case (.South, .South): return true
case (.East, .East): return true
case (.West, .West): return true
default: return false
}
}
Now you can test for equality or inequality, like this:
let otherNorth = CompassPoint.North(2)
println(littleNorth == bigNorth) // false
println(littleNorth == otherNorth) // true

Resources