Using AnyGenerator with Swift 2.2+ (“for in” loop

2019-02-21 05:31发布

问题:

Previously I was using the following function to make my custom class conform to the SequenceType protocol:

func generate() -> AnyGenerator<UInt32> {

    var nextIndex = 0

    return anyGenerator {
        if (nextIndex > self.scalarArray.count-1) {
            return nil
        }
        return self.scalarArray[nextIndex++]
    }
}

This is a similar implementation to the accepted answers to these two questions:

  • Add "for in" support to iterate over Swift custom classes
  • Add 'for...in' support to a class in Swift 2

But after a Swift 2.2 update...

'++' is deprecated: it will be removed in Swift 3

func generate() -> AnyGenerator<UInt32> {

    var nextIndex = 0

    return AnyGenerator {
        if (nextIndex > self.scalarArray.count-1) {
            return nil
        }
        nextIndex += 1
        return self.scalarArray[nextIndex]
    }
}

But this throws an Index out of range error because I actually need to use the pre-incremented index and then increment it after the return.

How does this work for AnyGenerator now in Swift? (Also, should I be decrementing rather than incrementing as the other two answers I linked to do?)

回答1:

(I assume that your code refers to struct ScalarString from Working with Unicode code points in Swift.)

You can do a Swift 2.2+ compatible "increment index after determining the return value" with defer:

func generate() -> AnyGenerator<UInt32> {

    var nextIndex = 0

    return AnyGenerator {
        if nextIndex >= self.scalarArray.count {
            return nil
        }
        defer {
            nextIndex += 1
        }
        return self.scalarArray[nextIndex]
    }
}

In your special case however, it would be easier to just forward the generator of the

private var scalarArray: [UInt32] = []

property, either directly:

func generate() -> IndexingGenerator<[UInt32]> {
    return scalarArray.generate()
}

or as a type-erased generator which forwards the next() method to the array generator:

func generate() -> AnyGenerator<UInt32> {
    return AnyGenerator(scalarArray.generate())
}


回答2:

Why don't you just introduced a dummy variable?

func generate() -> AnyGenerator<UInt32> {

    var nextIndex = 0

    return AnyGenerator {
        if (nextIndex > self.scalarArray.count-1) {
            return nil
        }
        let next = self.scalarArray[nextIndex]
        nextIndex += 1
        return next
}