2 min read

思考 FizzBuzz

如果你沒看過 FizzBuzz,題目是這樣的:

給定一串從 1 到 n 的數字:
a. 如果是 3 的倍數,就印出 “Fizz”
b. 如果是 5 的倍數,就印出 “Buzz”
c. 如果同時是 3 和 5 的倍數,就印出 “FizzBuzz”
d. 將印出的結果回傳為一個矩陣。

一般來說,最直覺的想法就是用 for 迴圈,然後裡面用很多 if 判斷式

const fizzBuzz = (n) => {
  const result = [];
  for ( let i = 0; i < n; i++ ) {
    if(n % 15 === 0) {
      result[n] = 'FizzBuzz';
    } else if(n % 3 === 0) {
      result[n] = 'Fizz';
    } else if(n % 5 === 0) {
      result[n] = 'Buzz';
    } else {
      result[n] = '' + n; // output should be String
    }
  }
  return result;
};

但是這樣太過 imperative,只是把解決步驟描述一遍,非常機械化,之後若回頭來看程式碼的時候,需要花比較多時間才能看懂。

如果要再進階一點,for 迴圈與 if 判斷處可以修改一下,變成

const fizzBuzz = (n) => {
    const condition = n => 
           ( n % 15 === 0 ) ? 'FizzBuzz' :
           ( n % 3 === 0 )  ? 'Fizz'     :
           ( n % 5 === 0 )  ? 'Buzz'     : '' + n;
    const result = [];
    Array.from(Array(n)).forEach((v, i) => {
         result[i] = condition(i + 1);
    });
    return result;
};

這是比較declarative 的寫法,著重在描述想要達成的目的,而非流程與步驟

所以可以看到,我除了用forEach代替for之外(盡量善用內建函式,也更不容易出錯),我還把條件判斷包成一個 function,好處是,在閱讀時可以讓我們對於整個fizzBuzz()的理解「分塊」進行,不需要讀完整段就能知道這段程式的「目的」。

再然後,我看到有人這樣寫

const fizzBuzz = (n) => new Array(n).fill(0).map((a, i) => 
  (++i % 3 ? '' : 'Fizz') + (i % 5 ? '' : 'Buzz') || '' + i);

簡潔有力,我跪!