Testing for enum value fails if one has associated value? - enums

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

Related

How to resolve whether pass objects via interface{} have not initializated fields

I have problem with resolve whether object which was pass as interface to function hasn't initializated fields, like object which was defined as just someObject{} is a empty, because all fields, has value 0, or nil
Problem becomes more complicated if I pass diffrent objects, because each object have diffrent type field value so on this moment I don't find universal way to this.
Example
func main(){
oo := objectOne{}
ot := objectTwo{}
oth := objectThree{"blah" , "balbal" , "blaal"}
resolveIsNotIntialized(oo)
resolveIsNotIntialized(ot)
resolveIsNotIntialized(oth)
}
func resolveIsNotIntialized(v interface{}) bool{
// and below, how resolve that oo and ot is empty
if (v.SomeMethodWhichCanResolveThatAllFiledIsNotIntialized){
return true
}
return false
}
I want to avoid usage switch statement like below, and additional function for each object, ofcorse if is possible.
func unsmartMethod(v interface{}) bool{
switch v.(type){
case objectOne:
if v == (objectOne{}) {
return true
}
// and next object, and next....
}
return false
}
As Franck notes, this is likely a bad idea. Every value is always initialized in Go. Your actual question is whether the type equals its Zero value. Generally the Zero value should be designed such that it is valid. The better approach would generally be to create an interface along the lines of:
type ZeroChecker interface {
IsZero() bool
}
And then attach that to whatever types you want to check. (Or possibly better: create an IsValid() test instead rather than doing your logic backwards.)
That said, it is possible to check this with reflection, by comparing it to its Zero.
func resolveIsNotIntialized(v interface{}) bool {
t := reflect.TypeOf(v)
z := reflect.Zero(t).Interface()
return reflect.DeepEqual(v, z)
}
(You might be able to get away with return v == z here; I haven't thought through all the possible cases.)
I don’t think there is a good reason (in idiomatic Go) to do what you are trying to do. You need to design your structs so that default values (nil, empty string, 0, false, etc.) are valid and represent the initial state of your object. Look at the source of the standard library, there are lots of examples of that.
What you are suggesting is easily doable via Reflection but it will be slow and clunky.
You could narrow the type which your function takes as an argement a little, not take an interface{} but accept one that allows you to check for non-zero values, say type intercae{nonZero() bool} as in the example code below. This will not tell you explicitly that it hasn't been set to the zero value, but that it is not zero.
type nonZeroed interface {
nonZero() bool
}
type zero struct {
hasVals bool
}
func (z zero) nonZero() bool {
return z.hasVals
}
type nonZero struct {
val int
}
func (nz nonZero) nonZero() bool {
return nz.val != 0
}
type alsoZero float64
func (az alsoZero) nonZero() bool {
return az != 0.0
}
func main() {
z := zero{}
nz := nonZero{
val: 1,
}
var az alsoZero
fmt.Println("z has values:", initialized(z))
fmt.Println("nz has values:", initialized(nz))
fmt.Println("az has values:", initialized(az))
}
func initialized(a nonZeroed) bool {
return a.nonZero()
}
Obviously as the type get more complex additional verification would need to be made that it was "nonZero". This type of pattern could be used to check any sort condition.

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

switch over enum / iota based type in golang

I defined an enumeration and struct type like so:
type NodeType int
const (
City NodeType = iota
Town
Village
)
type AreaNode struct {
Location Coord2D
Type NodeType
}
and now I'm iterating over a series of nodes that each have a type
if node, ok := area.Nodes[coord]; ok {
switch node.Type {
case node.Type == City:
// do something for City
case node.Type == Town:
// do something for Town
case node.Type == Outpost:
// do something for Outpost
}
}
However I'm getting an error: incompatible types in binary expression.
How can I resolve this?
you either do a switch with no value, and put comparison expressions in each case, or you treat each case as a == for the checked value. e.g.:
if node, ok := area.Nodes[coord]; ok {
switch node.Type {
case City:
// do something for City
case Town:
// do something for Town
case Outpost:
// do something for Outpost
}
}
The other switch syntax is used when you're switching between conditions that are not based on a single value. e.g.
switch {
case node.Type == City:
// do something for City
case node.OtherParam == "foo":
///
}
Which means basically you're switching between binary conditions. Personally, I use it just to remove clutter from long if/else blocks that don't rely on a single value, but I rarely use it.

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

The Swift Programming Language Enumerations Experiment

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

Resources