Swift's SequenceType
is a useful means of generating a sequence of values, and it makes it particularly useful iterate over these values.
I don't really have much experience with these SequenceType
types, so I wanted to implement my own for some practice and learning. What better sequence to take a look at than a Fizz Buzz sequence, right?
I wanted to make this Fizz Buzz a little special though. I wanted the user to define any sort of rules and add as many tests as they wanted. We just pair each test with a word, pass an array of these test-word pairs, and let the sequence do all the work.
So, to start out, I create custom types for the "Test" and the test-word "Pair":
typealias FizzBuzzRule = (Int) -> Bool
typealias FizzBuzzPair = (test: FizzBuzzRule, word: String)
So using a normal FizzBuzz example, we'd create the ordinary "Fizz" and "Buzz" tests like this:
let fizzTest = { (i: Int) -> Bool in return i % 3 == 0 } let buzzTest = { (i: Int) -> Bool in return i % 5 == 0 } let fizzPair: FizzBuzzPair = (fizzTest, "Fizz") let buzzPair: FizzBuzzPair = (buzzTest, "Buzz") let pairs = [fizzPair, buzzPair]
But of course, we can create any sort of rules we want. These are just examples, and as we see the rest of the code, we'll see how using these example rules will produce the standard "FizzBuzz" problem results.
The next step is writing a function to apply the rules and produce the required output. For that, I wrote the fizzBuzzify
function:
func fizzBuzzify(value: Int, fizzBuzzPairs: [FizzBuzzPair]) -> String {
var retnValue: String? = nil
for pair in fizzBuzzPairs {
if pair.test(value) {
retnValue = (retnValue ?? "") + pair.word
}
}
return retnValue ?? String(value)
}
So now, we can pass any value and any array of Test-Word pairs, and build our FizzBuzz-type string simply using this function.
Already, we could do something like this:
for x in 1...100 { println(fizzBuzzify(value, pairs)) }
But, I wanted to go one step further and improve this into a sequence which generates the values for us, so I needed to create FizzBuzzSequence
as a SequenceType
:
struct FizzBuzzSequence: SequenceType {
let startValue: Int
let endValue: Int
let pairs: [FizzBuzzPair]
init(start: Int = 1, end: Int = 100, pairs: [FizzBuzzPair]) {
self.startValue = start
self.endValue = end
self.pairs = pairs
}
init(start: Int = 1, end: Int = 100, pairs: FizzBuzzPair...) {
self.init(start: start, end: end, pairs: pairs)
}
func generate() -> GeneratorOf<String> {
var value: Int = self.startValue
return GeneratorOf<String> {
return (value <= self.endValue) ? fizzBuzzify(value++, self.pairs) : nil
}
}
}
And now, that we've put it all together, it can be used as simply as:
for fizzBuzzValue in FizzBuzzSequence(start: 1, end: 100, pairs: pairs) { println(fizzBuzzValue) }
And assuming pairs
is the same array of FizzBuzzPair
that we set up earlier, this will have the exact same results as any other FizzBuzz program you'd expect to see.
But we can now start at any value, end at any value, and set up any rules we want.
I'm looking for general comments on Swiftiness of this code, as well as double checking efficiency of the program in general. Am I even using the SequenceType
how it's intended to be used?
For clarify, below is the full set of code to be reviewed put together (it was split up by commentary above):
typealias FizzBuzzRule = (Int) -> Bool
typealias FizzBuzzPair = (test: FizzBuzzRule, word: String)
func fizzBuzzify(value: Int, fizzBuzzPairs: [FizzBuzzPair]) -> String {
var retnValue: String? = nil
for pair in fizzBuzzPairs {
if pair.test(value) {
retnValue = (retnValue ?? "") + pair.word
}
}
return retnValue ?? String(value)
}
struct FizzBuzzSequence: SequenceType {
let startValue: Int
let endValue: Int
let pairs: [FizzBuzzPair]
init(start: Int = 1, end: Int = 100, pairs: [FizzBuzzPair]) {
self.startValue = start
self.endValue = end
self.pairs = pairs
}
init(start: Int = 1, end: Int = 100, pairs: FizzBuzzPair...) {
self.init(start: start, end: end, pairs: pairs)
}
func generate() -> GeneratorOf<String> {
var value: Int = self.startValue
return GeneratorOf<String> {
return (value <= self.endValue) ? fizzBuzzify(value++, self.pairs) : nil
}
}
}