Swift Set of Structure Types

Swift Set of Structure Types

Update: As of Swift 4.1 (Xcode 9.4) the compiler can synthesize
the == and hashValue methods if all members of the struct are
Equatable/Hashable. Therefore is suffices to declare the conformance:

struct Cube: Hashable {
var x: Int
var y: Int
var z: Int
var width: Int
}

Previous answer for older Swift versions:

First of all, Hashable extends Equatable, so you must implement
a == operator which compares two values, using all properties
which uniquely identify a cube:

func ==(lhs: Cube, rhs: Cube) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.width == rhs.width
}

The Hashable protocol only requires that

x == y implies x.hashValue == y.hashValue

so

var hashValue: Int {
return 0
}

would be a valid (and working) implementation. However, this would
put all objects in the same hash bucket of a set (or dictionary),
which is not effective. A better implementation is for example

struct Cube: Hashable {
var x: Int
var y: Int
var z: Int
var width: Int

var hashValue: Int {
return x.hashValue ^ y.hashValue ^ z.hashValue ^ width.hashValue
}
}

Here the "XOR" operator ^ is chosen because it cannot overflow.
You could also use the "overflow operator" &+.

More sophisticated hash functions would be able to distinguish
different values better, so that the set operations become faster.
On the other hand, the computation of the hash function itself
would be slower. Therefore I would look for a "better" hash function
only if the set operations turn out to be a performance bottleneck in your program.

Why Choose Struct Over Class?

According to the very popular WWDC 2015 talk Protocol Oriented Programming in Swift (video, transcript), Swift provides a number of features that make structs better than classes in many circumstances.

Structs are preferable if they are relatively small and copiable because copying is way safer than having multiple references to the same instance as happens with classes. This is especially important when passing around a variable to many classes and/or in a multithreaded environment. If you can always send a copy of your variable to other places, you never have to worry about that other place changing the value of your variable underneath you.

With Structs, there is much less need to worry about memory leaks or multiple threads racing to access/modify a single instance of a variable. (For the more technically minded, the exception to that is when capturing a struct inside a closure because then it is actually capturing a reference to the instance unless you explicitly mark it to be copied).

Classes can also become bloated because a class can only inherit from a single superclass. That encourages us to create huge superclasses that encompass many different abilities that are only loosely related. Using protocols, especially with protocol extensions where you can provide implementations to protocols, allows you to eliminate the need for classes to achieve this sort of behavior.

The talk lays out these scenarios where classes are preferred:

  • Copying or comparing instances doesn't make sense (e.g., Window)
  • Instance lifetime is tied to external effects (e.g., TemporaryFile)
  • Instances are just "sinks"--write-only conduits to external state (e.g.CGContext)

It implies that structs should be the default and classes should be a fallback.

On the other hand, The Swift Programming Language documentation is somewhat contradictory:

Structure instances are always passed by value, and class
instances are always passed by reference. This means that they are
suited to different kinds of tasks. As you consider the data
constructs and functionality that you need for a project, decide
whether each data construct should be defined as a class or as a
structure.

As a general guideline, consider creating a structure when one or more
of these conditions apply:

  • The structure’s primary purpose is to encapsulate a few relatively simple data values.
  • It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an
    instance of that structure.
  • Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced.
  • The structure does not need to inherit properties or behavior from another existing type.

Examples of good candidates for structures include:

  • The size of a geometric shape, perhaps encapsulating a width property and a height property, both of type Double.
  • A way to refer to ranges within a series, perhaps encapsulating a start property and a length property, both of type Int.
  • A point in a 3D coordinate system, perhaps encapsulating x, y and z properties, each of type Double.

In all other cases, define a class, and create instances of that class
to be managed and passed by reference. In practice, this means that
most custom data constructs should be classes, not structures.

Here it is claiming that we should default to using classes and use structures only in specific circumstances. Ultimately, you need to understand the real world implication of value types vs. reference types and then you can make an informed decision about when to use structs or classes. Also, keep in mind that these concepts are always evolving and The Swift Programming Language documentation was written before the Protocol Oriented Programming talk was given.

Swift: map set of values A to B and B to A

A bijective function from a set to itself is a permutation. If the set consists of consecutive integers starting at zero then the permutation can be represented as an array.

In your case, the mapping from [0, 1, 2] to itself defined by

0 -> 1, 1 -> 2, 2 -> 0

would be represented as the array [1, 2, 0]. The “left-to-right” mapping then becomes a subscript operation:

let perm = [1, 2, 0]

print(perm[1]) // 2

The “right-to-left” mapping is the inverse permutation, and can also be represented as an array:

func inversePermution(of perm: [Int]) -> [Int]? {
var inverse: [Int] = Array(repeating: -1, count: perm.count)
for (idx, elem) in perm.enumerated() {
// Check for valid entries:
guard elem >= 0 && elem < perm.count else { return nil }
// Check for duplicate entries:
guard inverse[elem] == -1 else { return nil }
// Set inverse mapping:
inverse[elem] = idx
}
return inverse
}

(This is just to demonstrate the general idea. Of course you can make this an Array extension method, or define a Permutation type with this and more methods.)

In your example:

if let invPerm = inversePermution(of: perm) {
print(invPerm) // [2, 0, 1]

print(invPerm[2]) // 1
}

Function to accept multiple struct types in Swift

Simply declare topEarningData as

var topEarningData = [TopModel]()

so that it meets the specification in ChartInfo. Then add instances of TopContentModel or TopPlatformModel as needed.

 36> var topEarningData = [TopModel]() 
topEarningData: [TopModel] = 0 values
37> topEarningData.append(TopContentModel())
38> topEarningData.append(TopPlatformModel())
39> topEarningData
$R1: [TopModel] = 2 values {
[0] = {
type = nil
contentType = nil
title = nil
total = nil
}
[1] = {
platform = nil
total = nil
type = nil
}
}
40> ChartInfo(title: "Top Earning Assets", type: nil, prefix: "$", labels: nil, values: nil, list: topEarningData, formatter: nil, special: nil, secondSpecial: nil, color: "#37e0af")
$R2: ChartInfo = {
title = "Top Earning Assets"
type = nil
prefix = "$"
labels = nil
values = nil
list = 2 values {
[0] = {
type = nil
contentType = nil
title = nil
total = nil
}
[1] = {
platform = nil
total = nil
type = nil
}
}
formatter = nil
special = nil
secondSpecial = nil
color = "#37e0af"
}
41>

Structs that refer to each other in Swift 3

The problem is that an Optional stores its Wrapped value inline (see Mike Ash's fantastic blog post for more info about this) – meaning that an Optional instance (regardless of whether it is nil or not) will occupy at least the same amount of memory as the type you wish to store in its .some case (the Wrapped type).

Thus, as your Pin struct has a property of type DetailedPin?, and DetailedPin has a property of type Pin?, infinite storage would be required in order to store these values inline.

The solution therefore is simply to add a layer of indirection. One way of doing this would be to make Pin and/or DetailedPin a reference type (i.e a class) as @dfri has suggested.

However, if you wish to keep the value semantics of Pin and DetailedPin, one option would be to create a wrapper type backed by a class instance to provide the necessary indirection:

/// Provides indirection for a given instance.
/// For value types, value semantics are preserved.
struct Indirect<T> {

// Class wrapper to provide the actual indirection.
private final class Wrapper {

var value: T

init(_ value: T) {
self.value = value
}
}

private var wrapper: Wrapper

init(_ value: T) {
wrapper = Wrapper(value)
}

var value: T {
get {
return wrapper.value
}
set {
// Upon mutation of value, if the wrapper class instance is unique,
// mutate the underlying value directly.
// Otherwise, create a new instance.
if isKnownUniquelyReferenced(&wrapper) {
wrapper.value = newValue
} else {
wrapper = Wrapper(newValue)
}
}
}
}

You can now just use the Indirect wrapper for one (or both) of your structs properties:

struct DetailedPin {
private var _pin = Indirect<Pin?>(nil)

// Convenience computed property to avoid having to say ".value" everywhere.
var pin: Pin? {
get { return _pin.value }
set { _pin.value = newValue }
}
}

struct Pin {
var detailedPin: DetailedPin?
var foo: String
}

var d = DetailedPin()
var p = Pin(detailedPin: d, foo: "foo")
d.pin = p

// testing that value semantics are preserved...
var d1 = d
d1.pin?.foo = "bar"

print(d.pin?.foo as Any) // Optional("foo")
print(d1.pin?.foo as Any) // Optional("bar")

How to select a property of a struct dynamically to pass as a parameter in a function in swift 4?

What you're looking for is Swift KeyPath.

In my Sets implementation (Zotz!), this is what I do. First, I use enums with Int raw values for the four card attributes. This allows me to represent any attribute as a common type (Int). I express those raw values as computed properties, and I vend a list of all four computed properties as an array of key paths (note, this is Swift 5):

public struct Card: Codable, CustomStringConvertible {

let itsColor : Color
let itsNumber : Number
let itsShape : Shape
let itsFill : Fill

var itsColorRaw : Int { return itsColor.rawValue }
var itsNumberRaw : Int { return itsNumber.rawValue }
var itsShapeRaw : Int { return itsShape.rawValue }
var itsFillRaw : Int { return itsFill.rawValue }
public static let attributes : [KeyPath<Card, Int>] = [
\itsColorRaw, \itsNumberRaw, \itsShapeRaw, \itsFillRaw
]
// ...
}

OK, so now the rule for whether a given triple of cards constitutes a valid match is easy to express:

private func isCorrectCards(_ cards:[Card]) -> Bool {
func evaluateOneAttribute(_ nn:[Int]) -> Bool {
let allSame = (nn[0] == nn[1]) && (nn[1] == nn[2])
let allDiff = (nn[0] != nn[1]) && (nn[1] != nn[2]) && (nn[2] != nn[0])
return allSame || allDiff
}
return Card.attributes.allSatisfy { att in
evaluateOneAttribute(cards.map{$0[keyPath:att]})
}
}

Even more elegant version suggested by @vacawama:

private func isCorrect(cards:[Card]) -> Bool {
return Card.attributes.allSatisfy { att in
Set(cards.map{$0[keyPath:att]}).count != 2
}
}


Related Topics



Leave a reply



Submit