Fizz Buzz も知らないプログラマーなんて... みたいな記事を読んだ。
知らんかったので、やる。
Fizz Buzz ?
最初のプレイヤーは「1」と数字を発言する。次のプレイヤーは直前のプレイヤーの次の数字を発言していく。ただし、3で割り切れる場合は「Fizz」(Bizz Buzzの場合は「Bizz」)、5で割り切れる場合は「Buzz」、両者で割り切れる場合(すなわち15で割り切れる場合)は「Fizz Buzz」(Bizz Buzzの場合は「Bizz Buzz」)を数の代わりに発言しなければならない。発言を間違えた者や、ためらった者は脱落となる。
このゲームをコンピュータ画面に表示させるプログラムとして作成させることで、コードが書けないプログラマ志願者を見分ける手法をJeff AtwoodがFizzBuzz問題 (FizzBuzz Question) として提唱した。
なるほど。
JavaScript で実装
❶ 配列を作るパターン
- オーソドックスに「連番の配列」を用意して評価。
function fizzBuzz(max) {
return Array(max).fill().map((val, idx) => {
const num = idx + 1;
if (num % 15 === 0) return 'FizzBuzz';
if (num % 3 === 0) return 'Fizz';
if (num % 5 === 0) return 'Buzz';
return num;
});
}
console.log(fizzBuzz(30));
// 結果:
// [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz", 16, 17, "Fizz", 19, "Buzz", "Fizz", 22, 23, "Fizz", "Buzz", 26, "Fizz", 28, 29, "FizzBuzz"]
(補足) 連番配列の作り方は、どれがいい?
計測した中では、Array(arrayLength).fill().map((val, idx) => idx)
が良さそう。
/**
* 関数を指定回数実行し、平均実行時間を取得する
*
* @param max 試行回数
* @param func 実行する関数
* @returns 平均実行時間 (ms)
*/
function getAvgRunTime(max, func) {
let count = 0
let timeArr = [];
while (count < max) {
let start = performance.now();
let result = func();
let end = performance.now();
timeArr.push(end - start);
count++;
}
return timeArr.reduce((acc, cur) => acc + cur) / timeArr.length;
}
// いろいろな「連番の配列を作る方法」を各 30 回実行し、それぞれの平均実行時間を出力
console.log(getAvgRunTime(30, () => Array(1000000).fill().map((val, idx) => idx) ));
console.log(getAvgRunTime(30, () => [...Array(1000000)].map((val, idx) => idx) ));
console.log(getAvgRunTime(30, () => Array.from(Array(1000000).keys()) ));
console.log(getAvgRunTime(30, () => [...Array(1000000).keys()] ));
console.log(getAvgRunTime(30, () => Array.from({length: 1000000}, (val, idx) => idx) ));
// apply 関数を使う方法は、生成する要素数が多いと Maximum call stack size exceeded などで失敗するので割愛
// console.log(getAvgRunTime(30, () => Array.apply(null, Array(1000000)).map((val, idx) => idx) ));
連番配列の生成方法 | Chrome v84 | Firefox v79 |
---|---|---|
Array(1000000).fill().map((val, idx) => idx) |
19 ms 👑 | 19 ms |
[...Array(1000000)].map((val, idx) => idx) |
19 ms 👑 | 22 ms |
Array.from(Array(1000000).keys()) |
28 ms | 14 ms |
[...Array(1000000).keys()] |
38 ms | 13 ms 👑 |
Array.from({length: 1000000}, (val, idx) => idx) |
53 ms | 15 ms |
❷ ジェネレーター関数を使うパターン
- メモリを無駄に使わないように。
function getFizzBuzzAnswer(num) {
if (num % 15 === 0) return 'FizzBuzz';
if (num % 3 === 0) return 'Fizz';
if (num % 5 === 0) return 'Buzz';
return num;
}
function* makeFizzBuzzIterator(start = 1, end = Infinity) {
let num = start;
while (num < end) {
yield getFizzBuzzAnswer(num);
num++;
}
return getFizzBuzzAnswer(num);
}
let iterator = makeFizzBuzzIterator(1, 30);
while (true) {
let result = iterator.next();
console.log(result.value);
if (result.done) break;
}