用函數處理函數,來將函數組合起來運用(function composition in Javascript)

 Fri, 24 Sep 2010 21:50:28 +0800

今天又想到,在Javascript中可以用函數來處理一群函數,建立之間的關係,組織起來使用會比較方便。

循序、間隔執行數個函數應該是蠻簡單的,比較複雜的是嵌套,例如 a(b(c())) 這樣的形式。要怎麼建立這樣的執行關係呢?今天花了一點時間想出怎麼做:

function Compositor(){
  var args = [];
  for (var i=0; i<arguments.length; i++) {
    args.push(arguments[i]);
  }
  return function(n) {//這樣會做出一個currying,來接收要處理的參數n
    var req = function(x) {
      if(args.length>0){
//如果不是最後一個函數,就遞迴下去
        return x(req(args.shift()));
      } else {
//如果是最後一個函數,就把要處理的參數n傳給他執行,結束遞迴
        return x(n);
      }
    };
    return req(args.shift());
  };
}
function a(n){return --n;};
function b(n){return n+3;};
function c(n){return n*n;};
var d = Compositor(a, b, a, c);
alert(d(4));// 結果是17
alert(a(b(a(c(4)))));// 對照組,執行的結果應該要跟這個相同,都是17

測試的結果都是17。要執行的函數依序傳入,由外而內形成類似「嵌套」的關係,最後一個函數會先執行,執行結果當作參數傳給外層的函數,外層函數執行結果再傳給更外層的函數當作參數等等,循環下去。這個結構看起來也有點像decorator,其實也能拿來這樣用。

有時間再來想想還有怎樣的「關係」可以把函數組合起來使用。


2010-9-25 22:33 補充:

一直在想這樣的函數關係叫做什麼,剛剛找了一下,在wikipedia發現這個:

看起來就是我想做的東西。chain也叫做fluent interface,通常會指另一種東西,還是改掉比較好。所以我把程式中的函數名稱從Chain改成Compositor。