-1

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:

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

Community
  • 1
  • 1
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393

2 Answers2

4

(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())
}
Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Your assumption is correct. Good memory. So the reason I can forward the generator in my particular case is because `UInt32` already has it's own predefined generator. Is that correct? – Suragch Apr 09 '16 at 03:44
  • 1
    @Suragch: No, the reason is that `Array` (or `CollectionType`) has a `generate()` method. And `AnyGenerator` has a `init(_ base: G)`. – Martin R Apr 09 '16 at 03:45
  • The `carList` variables in the two answers I mentioned in my question ([here](http://stackoverflow.com/a/26219833) and [here](http://stackoverflow.com/a/31730379/)) are also `Array`s (of the `Car` class). Could one also just forward the array generator there, too? And if so, when would you use the more general answer you gave here (the one using `defer`)? Only when creating a custom `CollectionType`? – Suragch Apr 09 '16 at 03:56
  • 1
    @Suragch: Actually [this answer](http://stackoverflow.com/a/27636938/1187415) does it already (which I did not notice before), only without the type erasure. The `defer` example was primarily meant as a direct counterpart to the to-be-replaced `return self.scalarArray[nextIndex++]`, but it can be useful when implementing custom sequence types. – Martin R Apr 09 '16 at 04:10
  • FYI, in Swift 3 `AnyGenerator` is renamed to [`AnyIterator`](http://swiftdoc.org/v3.0/type/AnyIterator/). – Franklin Yu Jul 22 '16 at 06:11
0

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
}
irkinosor
  • 766
  • 12
  • 26