In my bid to introduce myself to the programming world, I imagined the best way is by solving the popular FizzBuzz problem .
A naive and intuitive implementation:
FizzBuzz - Naive
[JavaScript]
const fizzbuzz = (n = 100) => {
for (let i = 1; i <= n; i++) {
if (i % 3 === 0 && i % 5 === 0)
console.log('FizzBuzz')
else if (i % 3 === 0)
console.log('Fizz')
else if (i % 5 === 0)
console.log('Buzz')
else
console.log(i)
}
}
fizzbuzz(100)
But the programmer in me will not be happy with this solution, for following reasons:
- There is a repetition of conditional logics.
- Additional logics cannot be easily added to existing solution, e.g. if divisible by 7, print baz.
- The starting value is fixed at 1.
- I would prefer to return values rather than `print` in the function.
FizzBuzz - Better
[JavaScript]
const range = (start, end) => Array.from(
{ length: end - start },
(v, k) => k + start
)
const fzReducer = i =>
(evalued, evaluator) => evalued + evaluator(i)
const fzMapper = evaluators =>
i => evaluators.reduce(fzReducer(i), '') || i
const fizzbuzz = (n, ...evaluators) => {
let [a, b] = Number.isInteger(evaluators[0])
? [n, evaluators.shift()]
: [1, n]
return range(a, b + 1).map(fzMapper(evaluators))
}
const createFZEvaluator = (n, msg) => i => i % n === 0 ? msg : ''
let evaluators = [
createFZEvaluator(3, 'Fizz'),
createFZEvaluator(5, 'Buzz')
]
let fizzbuzzes = fizzbuzz(100, ...evaluators)
fizzbuzzes.forEach(fz => console.log(fz))
Although this code manages to counter many issues caused by the naive implementation, it still passes around a 2 arrays. Once when `range` method returns an array, second when `fizzbuzz` returns array of result. This issue can be resolved using Generators
A simple solution using Generators.
FizzBuzz - Generators
[JavaScript]
function* range(start, end, interval = 1) {
for(let i = start; i < end; i = i + interval) {
yield i
}
}
function getFizzbuzz(getEvaluators) {
return function* fizzbuzz(min, max) {
let [a, b] = max === void 0 ? [1, min] : [min, max]
for(let j of range(a, b + 1)) {
let evaluated = ''
for(let evaluator of getEvaluators()) {
evaluated = evaluated + evaluator(j)
}
yield (evaluated || j)
}
}
}
const createFZEvaluator = ([n, msg]) => i => i % n === 0 ? msg : ''
const evaluators = [
[3, 'Fizz'],
[5, 'Buzz']
].map(createFZEvaluator)
function* getEvaluators() {
for(let evaluator of evaluators) {
yield evaluator
}
}
const fizzbuzz = getFizzbuzz(getEvaluators)
let fizzbuzzes = fizzbuzz(100, 110)
for (let fz of fizzbuzzes) { console.log(fz) }