patternswiftModerate
Ultimate FizzBuzz
Viewed 0 times
ultimatefizzbuzzstackoverflow
Problem
Swift's
I don't really have much experience with these
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":
So using a normal FizzBuzz example, we'd create the ordinary "Fizz" and "Buzz" tests like this:
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
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
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
can be simplified to
In
the type annotation
and the generic type `
from a range of integers to a sequence of strings, so the same
would be achieved with
suggestions.
An Optional has an implicit initial value of
nil, so var retnValue: String? = nilcan 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 mappingfrom 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? = nilvar 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.