How do I delegate a call to [0..<n] in swift?

2019-02-10 16:13发布

问题:

Background

For convenience, I used this alias:

typealias Deck = [Int]

My needs are expanding so I have now converted my code to:

class Deck
{
  var deck : [Int]
  // ... other members
}

I am able to delegate most of my calls through to self.deck, but after googling around somewhat, I am still having trouble figuring out how to delegate this call:

let deck = Deck()
for i in deck[0..<5] { }   // <--- PROBLEMS HERE

Question

How do I implement the delegation of this call?

I think it has something to do with subscript and range (or maybe sequence?), but I had no luck googling the intersection of these two topics.

回答1:

For deck[0..<5] you need to implement the Sliceable protocol, which in turn requires CollectionType and SequenceType.

The following example implements MutableCollectionType so that setting elements is forwarded to the array as well:

class Deck
{
    typealias DeckValueType = Int
    typealias DeckIndexType = Array<DeckValueType>.Index

    var deck : [DeckValueType] = [0, 1, 2, 3, 4, 5, 6] // just for demonstration ...
}

extension Deck : SequenceType {
    func generate() -> IndexingGenerator<[DeckValueType]> {
        return deck.generate()
    }
}

extension Deck: MutableCollectionType {
    var startIndex : DeckIndexType { return deck.startIndex }
    var endIndex : DeckIndexType { return deck.endIndex }

    subscript(index: DeckIndexType) -> DeckValueType {
        get {
            return deck[index]
        }
        set(newValue) {
            deck[index] = newValue
        }
    }
}

extension Deck : Sliceable {
    subscript (bounds: Range<DeckIndexType>) -> ArraySlice<DeckValueType> {
        get {
            return deck[bounds]
        }
    }
}

And then it works:

let deck = Deck()
for i in deck[0..<5] {
    println(i)
}   

The type aliases are not strictly necessary, but they make it easier to distinguish whether Int is used as the type of an array element or as the type for an array index.

Also you can put all the code into the class definition. I have chosen separate extensions to make clear which method is needed for which protocol.