HiveBrain v1.2.0
Get Started
← Back to all entries
patternswiftModerate

Ultimate FizzBuzz

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
ultimatefizzbuzzstackoverflow

Problem

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(fizzB

Solution

Your code looks good to me, and I have only some minor remarks and
suggestions.

An Optional has an implicit initial value of nil, so

var retnValue: String? = nil


can be simplified to

var retnValue: String?


In

func generate() -> GeneratorOf {
    var value: Int = self.startValue
    return GeneratorOf {
        return (value <= self.endValue) ? fizzBuzzify(value++, self.pairs) : nil
    }
}


the type annotation : Int and the first self are not needed,
and the generic type ` can be inferred from the compiler
in the second instance:

func generate() -> GeneratorOf {
    var value = startValue
    return GeneratorOf {
        return (value <= self.endValue) ? fizzBuzzify(value++, self.pairs) : nil
    }
}


Instead of named functions "fizzTest", "buzzTest", closures can be
used to define the tests, and the entire rule set can be written as

let pairs : [FizzBuzzPair] = [
    ( { $0 % 3 == 0 } , "Fizz"),
    ( { $0 % 5 == 0 } , "Buzz")
]


The
typealias FizzBuzzRule is used only in one place, so you could
drop that and define

typealias FizzBuzzPair = (test: (Int) -> Bool, word: String)


instead. I would probably define a
struct instead of a tuple:

struct FizzBuzzRule {
    let test : (Int) -> Bool
    let word : String
}

let rules = [
    FizzBuzzRule(test: { $0 % 3 == 0 }, word: "Fizz") ,
    FizzBuzzRule(test: { $0 % 5 == 0 }, word: "Buzz")
]


because compiler diagnostics and autocompletion works better with struct,
but that may be a matter of taste.

Finally note that
FizzBuzzSequence` essentially does a mapping
from a range of integers to a sequence of strings, so the same
would be achieved with

let fbSequence = lazy(1 ... 100).map { fizzBuzzify($0, pairs) }
for fbValue in fbSequence {
    println(fbValue)
}

Code Snippets

var retnValue: String? = nil
var retnValue: String?
func generate() -> GeneratorOf<String> {
    var value: Int = self.startValue
    return GeneratorOf<String> {
        return (value <= self.endValue) ? fizzBuzzify(value++, self.pairs) : nil
    }
}
func generate() -> GeneratorOf<String> {
    var value = startValue
    return GeneratorOf {
        return (value <= self.endValue) ? fizzBuzzify(value++, self.pairs) : nil
    }
}
let pairs : [FizzBuzzPair] = [
    ( { $0 % 3 == 0 } , "Fizz"),
    ( { $0 % 5 == 0 } , "Buzz")
]

Context

StackExchange Code Review Q#91663, answer score: 19

Revisions (0)

No revisions yet.