I'm trying to implement a SequenceType / GeneratorType example and getting an error that doesn't quite make sense.
Here's the code:
// Here's my GeneratorType - it creates a random-number Generator:
struct RandomNumberGenerator:GeneratorType {
typealias Element = Int
mutating func next() -> Element? {
return Int(arc4random_uniform(100))
}
}
When I call this (in Playgrounds) it works perfectly well:
var randyNum = RandomNumberGenerator()
randyNum.next() // this shows a valid random number in the Gutter
// And calling it from within a println also works:
println("randyNum = \(randyNum.next()!)")
So far so so good.
Next is the SequenceType:
struct RandomNumbersSequence:SequenceType {
typealias Generator = RandomNumberGenerator
var numberOfRandomNumbers:Int
init(maxNum:Int) {
numberOfRandomNumbers = maxNum
}
func generate() -> Generator {
for i in 1...numberOfRandomNumbers {
var randNum = Generator()
randNum.next()
return randNum
}
}
}
That's what's generating an error: 'Type RandomNumberSequence' does not conform to protocol 'SequenceType'
. (Xcode is showing this error right on that first line of the declaration struct RandomNumbersSequence:SequenceType
statement.)
I actually think the logic of my for
loop may be wrong - meaning I won't get the results I actually want - but regardless, in terms of satisfying what the SequenceType
Protocol is requiring, I think I got that much right.
So what's causing this error?
This isn’t quite how generators work. Assuming you want to serve up a set number of random numbers, you have the right idea about taking a maximum as a parameter, but you need to store that in the generator, as well, plus some state for where it is up to.
The idea with a generator is that it stores its state, and every time you call next()
you return the next element. So if you want to generate a run of up to n
numbers, you could do something like the following:
struct RandomNumberGenerator: GeneratorType {
let n: Int
var i = 0
init(count: Int) { self.n = count }
mutating func next() -> Int? {
if i++ < n {
return Int(arc4random_uniform(100))
}
else {
return nil
}
}
}
Note, you don’t need a for
loop here. Just each time next()
is called, i
is incremented, until it reaches the max, then the generator starts returning nil
.
The purpose of SequenceType
is to serve up fresh generators:
struct RandomNumberSequence: SequenceType {
let n: Int
init(count: Int) { self.n = count }
func generate() -> RandomNumberGenerator {
return RandomNumberGenerator(count: n)
}
}
Given this, you can now use it to generate a sequence of a fixed number of random integers:
let seq = RandomNumberSequence(count: 3)
for x in seq {
// loops 3 times with x being a new random number each time
}
// every time you use seq, you get a new set of 3 numbers
",".join(map(seq,toString)) // prints 3 comma-separated random nums
// but when you create a generator, it gets “used up”
var gen = seq.generate()
println(gen.next()) // prints a random number
println(gen.next()) // prints another random number
println(gen.next()) // prints the third
println(gen.next()) // prints nil
println(gen.next()) // and will keep printing nil
gen = seq.generate()
println(gen.next()) // will print the first of a new set of 3 numbers
Creating these stateful generators is quite a common problem, so the standard library has a helper struct, GeneratorOf
, that allows to you skip defining them. It takes a closure that each time it is called should return the next value to generate:
struct RandomNumbersSequence: SequenceType {
let maxNum: Int
init(maxNum: Int) { self.maxNum = maxNum }
func generate() -> GeneratorOf<Int> {
// counter to track how many have been generated
var n = 0
return GeneratorOf {
// the closure “captures” n
if n++ < self.maxNum {
return Int(arc4random_uniform(100))
}
else {
return nil
}
}
}
}