Is it possible to have a subscript in Swift that has different signatures for the getter and setter?
For example, I want the getter to return a Set<Int> and the setter to take an Int (not a Set<Int>).
This code won't compile but it gives you an idea of what I'm trying to do:
struct Foo{
subscript(index: Int)->String{
get{
return "bar" // returns a String
}
set(newValue: String?){ // takes a String? instead of a String
print(newValue)
}
}
}
How can I do this?
This is very ugly, and I strongly discourage you from doing so, but technically this is possible:
struct Foo {
subscript(concreteValueFor index: Int) -> String {
return "Get concrete \(index)"
}
subscript(optionalValueFor index: Int) -> String? {
get { return nil }
set { print("Set optional \(index)") }
}
}
var foo = Foo()
foo[concreteValueFor: 1] // Returns "Get concrete 1"
foo[optionalValueFor: 2] = "" // Prints "Set optional 2"
For a multimap some time ago I made something like this:
public struct Multimap<Key: Hashable, Value: Hashable>: CollectionType {
public typealias _Element = Set<Value>
public typealias Element = (Key, _Element)
public typealias Index = DictionaryIndex<Key, _Element>
public typealias Generator = DictionaryGenerator<Key, _Element>
private typealias Storage = [Key: _Element]
private var storage = Storage()
public var startIndex: Index { return storage.startIndex }
public var endIndex: Index { return storage.endIndex }
public subscript(position: Index) -> _Element { return storage[position].1 }
public subscript(position: Index) -> Element { return storage[position] }
subscript(key: Key) -> Set<Value> {
get { return storage[key] ?? Set<Value>() }
set { storage[key] = newValue.isEmpty ? nil : newValue }
}
public func generate() -> Generator { return storage.generate() }
}
Usage:
var foo = Multimap<Int, String>()
foo[0] // Returns an emtpy Set<String>
foo[0].insert("Ook") // Inserts a value at index 0
foo[0].insert("Eek")
foo[0] // Now this returns a set { "Ook", "Eek" }
foo[1].insert("Banana")
foo[1].insert("Book")
foo[0].unionInPlace(foo[1])
foo[0] // Returns a set { "Banana", "Ook", "Eek", "Book" }
Related
Marking CacheManager with 'class' solved my problem.
Here's the case: a simple cacher, the mutating get is not what I want, so how should do for the reference types or just class type?
protocol Cacher {
associatedtype K
associatedtype V
subscript (key: K) -> V? {get set}
}
protocol MemoryCacher: Cacher {}
protocol FileCacher: Cacher {}
updated. add PrimayCacher | SecondaryCacher associatedtypes
protocol CacheManager {
associatedtype Key
associatedtype Value
associatedtype PrimaryCacher: MemoryCacher where PrimaryCacher.K == Key, PrimaryCacher.V == Value
associatedtype SecondaryCacher: FileCacher where SecondaryCacher.K == Key, SecondaryCacher.V == Value
var primaryCacher: PrimaryCacher { get set }
var secondaryCacher: SecondaryCacher{ get set }
subscript(key: Key) -> Value? { get set }
}
//try to provide a default subscript conformance, but actually not for the `mutating` get
extension CacheManager {
subscript(key: Key) -> Value? {
mutating get {
guard let result = primaryCacher[key] else {
if let value = secondaryCacher?[key] {
primaryCacher[key] = value // the mutating is required for this
return value
}
return nil
}
return result
}
set {
primaryCacher[key] = newValue
secondaryCacher?[key] = newValue
}
}
}
You just need to add a type restriction to AnyObject, which all classes conform to.
extension CacherManager where Self: AnyObject {
subscript(key: Key) -> Value? {
get {
guard let result = primaryCacher[key] else {
if let value = secondaryCacher?[key] {
primaryCacher[key] = value // the mutating is required for this
return value
}
return nil
}
return result
}
set {
primaryCacher[key] = newValue
secondaryCacher?[key] = newValue
}
}
}
I also had to slightly modify your CacheManager declaration for the code to compile and add the required protocol methods to MemoryCacher and FileCacher that you omitted from the question.
protocol Cacher {
associatedtype K
associatedtype V
subscript (key: K) -> V? {get set}
}
class MemoryCacher<K, V>: Cacher {
private var value: V?
subscript(key: K) -> V? {
get {
return value
}
set {
value = newValue
}
}
}
class FileCacher<K, V>: Cacher {
private var value: V?
subscript(key: K) -> V? {
get {
return value
}
set {
value = newValue
}
}
}
protocol CacheManager {
associatedtype Key
associatedtype Value
var primaryCacher: MemoryCacher<Key,Value> { get set }
var secondaryCacher: FileCacher<Key,Value>? { get set }
subscript(key: Key) -> Value? { get set }
}
I have defined a struct for the Stack:
struct Stack<T> {
private(set) var elements = [T]()
var isEmpty: Bool { return elements.isEmpty }
mutating func push(newElement: T) {
elements.append(newElement)
}
mutating func pop() -> T {
return elements.removeLast()
}
func top() -> T? {
return elements.last
}
}
When I use the method to get the last element and check if it is in the collection:
if operators.contains(stack.top()!) {
//do smth
}
compiler raises an error: "Ambiguous reference to member 'contains'"
Update: The collection is defined as:
struct Operator: OperatorType {
let name: String
let precedence: Int
let associativity: Associativity
// same operator names are not allowed
var hashValue: Int { return "\(name)".hashValue }
init(_ name: String, _ precedence: Int, _ associativity: Associativity) {
self.name = name; self.precedence = precedence; self.associativity = associativity
}
}
And init with:
let operators: Set <Operator> = [
Operator("%", 4, .Right),
Operator("*", 3, .Left),
Operator("/", 3, .Left),
Operator("+", 2, .Left),
Operator("-", 2, .Left)
]
What am I doing wrong?
This is because the contains method you are looking for only exists where Array.Element : Equatable
Taken from the stdlib.
extension Array where Element : Equatable {
...
public func contains(_ element: Element) -> Bool
}
Assuming someCollection is Array<Stack> then you would need to change Stack to:
struct Stack<T: Equatable> : Equatable {
And with Swift 4.1 the Equatable conformance is handled automatically. Source
I have two protocols and a generic struct:
public protocol OneDimensionalDataPoint {
/// the y value
var y: Double { get }
}
public protocol TwoDimensionalDataPoint: OneDimensionalDataPoint {
/// the x value
var x: Double { get }
}
public struct DataSet<Element: OneDimensionalDataPoint> {
/// the entries that this dataset represents
private var _values: [Element]
//...implementation
}
extension DataSet: MutableCollection {
public typealias Element = OneDimensionalDataPoint
public typealias Index = Int
public var startIndex: Index {
return _values.startIndex
}
public var endIndex: Index {
return _values.endIndex
}
public func index(after: Index) -> Index {
return _values.index(after: after)
}
public subscript(position: Index) -> Element {
get{ return _values[position] }
set{ self._values[position] = newValue }
}
}
There is a large number of methods that apply to DataSet only when it's Element is a TwoDimensionalDataPoint. So I made an extension like so:
extension DataSet where Element: TwoDimensionalDataPoint {
public mutating func calcMinMaxX(entry e: Element) {
if e.x < _xMin {
_xMin = e.x
}
if e.x > _xMax {
_xMax = e.x
}
}
}
The compiler doesn't like this, and says:
Value of type 'DataSet.Element' (aka
'OneDimensionalDataPoint') has no member 'x'
Shouldn't this be fine since I constrained Element to TwoDimensionalDataPoint in the extension?
After I popped it into Xcode I was able to get a better understanding of what was going on,
Your issue is your type alias is overriding your generic type,
Rename your generic name to T and assign Element to T
public typealias Element = T
or your typealias like:
public typealias DataElement = OneDimensionalDataPoint
or just drop the typealias all together.
Im experimenting with subscripts and generics, and what I am trying to do is this:
heap[0]!.hasLeftChild
Where my heap class looks something like this:
public class Heap<T> where T:HeapSubscriptable {
private var size = Int()
private var valueList = [T]()
public var capacity = 3
// subscript setter
subscript(i: Int) -> T? {
if i < self.size {
return valueList[i]
} else {
return nil
}
}
...
}
To allow this i made a protocol extension:
public protocol HeapSubscriptable {
var hasLeftChild: Bool{ get }
}
extension HeapSubscriptable {
public var hasLeftChild: Bool {
// return (self.size > 1+i*2 ) // <-- I want to get a hold of this i
}
}
extension Int : HeapSubscriptable{}
But to calculate this I need access to the subscript parameter i, so that i can use its index in my array and do magic on it and see if it actually has a left child. Is there away to access it?
I made a (very basic) BinaryTree protocol:
public enum BinaryTreeChildSide {
case left, right
}
public protocol BinaryTree {
associatedtype Element
associatedtype Index
func child(of index: Index, side: BinaryTreeChildSide) -> Index?
var rootIndex: Index? { get }
subscript(position: Index) -> Element { get }
}
For a basic iterative in-order traversal, I made a BinaryTreeIterator (note that I don't implement Sequence just yet):
public extension BinaryTree {
func makeIterator() -> BinaryTreeIterator<Self> {
return BinaryTreeIterator(self)
}
}
public struct BinaryTreeIterator<Tree: BinaryTree>: IteratorProtocol {
private let tree: Tree
private var stack: [Tree.Index]
private var index: Tree.Index?
private init(_ tree: Tree) {
self.tree = tree
stack = []
index = tree.rootIndex
}
public mutating func next() -> Tree.Element? {
while let theIndex = index {
stack.append(theIndex)
index = tree.child(of: theIndex, side: .left)
}
guard let currentIndex = stack.popLast() else { return nil }
defer { index = tree.child(of: currentIndex, side: .right) }
return tree[currentIndex]
}
}
Implementing a binary heap to this protocol is also pretty straight-forward:
public struct BinaryHeap<Element> {
private var elements: [Element]
public init(_ elements: [Element]) {
self.elements = elements
}
}
extension BinaryHeap: BinaryTree {
private func safeIndexOrNil(_ index: Int) -> Int? {
return elements.indices.contains(index) ? index : nil
}
public func child(of index: Int, side: BinaryTreeChildSide) -> Int? {
switch side {
case .left: return safeIndexOrNil(index * 2 + 1)
case .right: return safeIndexOrNil(index * 2 + 2)
}
}
public var rootIndex: Int? { return safeIndexOrNil(0) }
public subscript(position: Int) -> Element {
return elements[position]
}
}
So far, so good. I can now make a simple heap and iterate through its elements:
let heap = BinaryHeap([4, 2, 6, 1, 3, 5, 7])
var iterator = heap.makeIterator()
while let next = iterator.next() {
print(next, terminator: " ")
}
// 1 2 3 4 5 6 7
This works, but of course the goal of implementing makeIterator() is to conform to Sequence. however, if I replace
public protocol BinaryTree {
with
public protocol BinaryTree: Sequence {
then the compiler complains that BinaryHeap doesn't implement Sequence because the associated type Iterator couldn't be inferred. If I manually specify the Iterator type with
extension BinaryHeap: BinaryTree {
typealias Iterator = BinaryTreeIterator<BinaryHeap>
...
}
then the compiler shows an error that Iterator circularly references itself. So that might be why the Iterator type couldn't be inferred.
Interestingly, it works if I wrap my custom BinaryTreeIterator in an AnyIterator instance:
public extension BinaryTree {
func makeIterator() -> AnyIterator<Element> {
return AnyIterator(BinaryTreeIterator(self))
}
}
let heap = BinaryHeap([4, 2, 6, 1, 3, 5, 7])
for number in heap {
print(number, terminator: " ")
}
// 1 2 3 4 5 6 7
Apple's own IndexingIterator seems to work in a similar fashion to my BinaryTreeIterator:
public struct IndexingIterator<
Elements : IndexableBase
// FIXME(compiler limitation):
// Elements : Collection
> : IteratorProtocol, Sequence {
...
}
From the source code. Maybe the problem I'm facing might also be because of the compiler limitation mentioned there, but I don't know for sure.
Is there a way to conform BinaryTree to Sequence without using AnyIterator?
This is the farthest I could take it. Now the compiler will still complain about the heap not containing any makeIterator member
(which I thought was included by default once someone conforms to sequence—wrong turns out one must conform to Sequence and IteratorProtocol for the default implementation) and having a next—but once you add those methods its smooth sailing.
So makeIterator + next method to make Mr/Mrs/Preferred Gender Pronoun compiler happy.
public enum BinaryTreeChildSide {
case left, right
}
public struct BinaryTreeIterator<Tree: BinaryTree>: Sequence, IteratorProtocol {
private let tree: Tree
private var stack: [Tree.Index]
private var index: Tree.Index?
private init(_ tree: Tree) {
self.tree = tree
stack = []
index = tree.rootIndex
}
public mutating func next() -> Tree.Element? {
while let theIndex = index {
stack.append(theIndex)
index = tree.child(of: theIndex, side: .left)
}
guard let currentIndex = stack.popLast() else { return nil }
defer { index = tree.child(of: currentIndex, side: .right) }
return tree[currentIndex]
}
}
public protocol BinaryTree: Sequence {
associatedtype Element
associatedtype Index
func child(of index: Index, side: BinaryTreeChildSide) -> Index?
var rootIndex: Index? { get }
subscript(position: Index) -> Element { get }
}
extension BinaryTree {
func makeIterator() -> BinaryTreeIterator<Self> {
return BinaryTreeIterator(self)
}
}
extension BinaryHeap {
private func safeIndexOrNil(_ index: Int) -> Int? {
return elements.indices.contains(index) ? index : nil
}
public func child(of index: Int, side: BinaryTreeChildSide) -> Int? {
switch side {
case .left: return safeIndexOrNil(index * 2 + 1)
case .right: return safeIndexOrNil(index * 2 + 2)
}
}
public var rootIndex: Int? { return safeIndexOrNil(0) }
public subscript(position: Int) -> Element {
return elements[position]
}
}
public struct BinaryHeap<Element> {
private var elements: [Element]
public init(_ elements: [Element]) {
self.elements = elements
}
}
let heap = BinaryHeap([4, 2, 6, 1, 3, 5, 7])
var iterator = heap.makeIterator()
while let next = iterator.next() {
print(next, terminator: " ")
}
Apparently it was a Swift bug: my code compiles fine using Swift 3.1.