從重複代碼說起#
Joel 以一個簡單的 JavaScript 範例開場:兩段幾乎相同的代碼,唯一的差別是一塊與「Spaghetti」有關,另一塊與「Chocolate Mousse」有關。用函數消除重複的道理大家都懂——更容易維護、可讀性更好、更容易重複利用。
函數作為參數的威力#
當你發現兩段代碼除了呼叫不同的函數(如 PutInPot vs BoomBoom)外完全一樣時,你需要一種方法能將函數作為參數傳入另一個函數:
function Cook(i1, i2, f) {
alert("get the " + i1);
f(i1);
f(i2);
}
Cook("lobster", "water", PutInPot);
Cook("chicken", "coconut", BoomBoom);這種能力叫做 first-class function(第一類函數)。你的程式語言如果無法將函數作為參數傳遞,你就無法享受現代程式設計環境帶來的全部好處。
匿名函數#
更進一步,如果你還沒有定義 PutInPot 或 BoomBoom,可以在呼叫處直接用 anonymous function(匿名函數)建立:
Cook("lobster", "water", function (x) {
alert("pot " + x);
});從 map 到 reduce#
map 函數#
- 對陣列的每一個元素進行某種操作是非常常見的模式
- 寫一個
map(fn, a)函數,就可以用map(function(x){return x*2;}, a)取代手動迴圈
reduce 函數#
sum和join看起來很像,都是將陣列的元素結合成一個值- 抽象為通用的
reduce(fn, a, init)函數
許多老舊的程式語言無法實現這些功能。C 語言雖有函數指標但使用不便;Java 要求你為每個 functor 建立一個只有一個方法的物件。如果你的語言要用到 functor,你最好去查一下能不能退貨。
通往 MapReduce 之路#
Joel 指出:map 函數處理陣列時,元素的遍歷順序其實無關緊要——可以從頭到尾,也可以從尾到頭,甚至可以分配到多個 CPU 並行處理。
- 想像有幾十萬台伺服器,上面有超級龐大的資料集
- 你可以在這些伺服器上運行
map函數,每台負責一小部分 - 你寫出的搜尋引擎代碼,只要呼叫
map加上一個字串搜尋器就行
這正是 Google 的 MapReduce 技術背後的原理。
Map和Reduce這兩個術語來自 Lisp 語言和函數式程式設計。如果你不懂函數式程式設計,你就無法發明出 MapReduce。純粹的函數式程式設計沒有副作用,因此可以直接用於並行計算。
結論#
那些具備「第一類函數」功能的程式語言,能讓你更容易完成進一步抽象代碼的任務。你的代碼體積更小、更緊湊、更容易重複利用、更方便擴展。Google 的許多應用程式都使用了 MapReduce 技術,一旦有人對底層的並行計算程式進行優化或消除 bug,所有應用程式都會受益。