関数プログラミング-組み合わせcomposse


関数プログラミングには、比較的重要な概念があります.関数の組み合わせです.複数の関数を組み合わせて、新しい関数を返します.呼び出し時、コンビネーション関数は順番に右から左に実行されます.右の関数が呼び出された後に戻った結果、左の関数のパラメータとして入ってきました.実行順序が厳密に保証されています.これもcomposeの主な特徴です.
入門ガイド
二つの関数を組み合わせる
composeは非常に簡単です.下記のコード例を通して、分かります.
function compose (f, g) {
    return function(x) {
        return f(g(x));
    }
}

var arr = [1, 2, 3],
    reverse = function(x){ return x.reverse()},
    getFirst = function(x) {return x[0]},
    compseFunc = compose(getFirst, reverse);
    
compseFunc(arr);   // 3
パラメータは関数間で「パイプ」を通して伝送されるように、一番右の関数で外部パラメータを受信し、その結果を左の関数に返し、最後に結果を出力します.
任意の関数を組み合わせます.
上の2つの関数を組み合わせたcomposeも、組み合わせの特徴を理解しました.次に、より多くの関数を組み合わせてみます.実際の応用では、入門紹介のコードほど簡単ではないからです.
主にいくつかのポイントに注意します.
  • は、argmentsの長さを利用して、すべての組合せ関数の個数
  • を得る.
  • reduceは全ての関数を巡回して実行します.
  •     var compose = function() {
          var args = Array.prototype.slice.call(arguments);
          
          return function(x) {
           if (args.length >= 2) {
           
              return args.reverse().reduce((p, c) => {
                return p = c(p)
             }, x)
             
           } else {
               return args[1] && args[1](x);
           }
          }
        }
       
        //            。
        var arr = [1, 2, 3],
        reverse = function(x){ return x.reverse()},
        getFirst = function(x) {return x[0]},
        trace = function(x) {  console.log('    :', x); return x}
        
        
        compseFunc = compose(trace, getFirst, trace, reverse);
        
    compseFunc(arr);   
     //     : (3) [3, 2, 1]
     //     : 3
     // 3
    このように実現されても、基本的には問題がなく、変数arrがパイプに入ってきてから、様々な操作を経て、結果が返ってきます.
    理解を深める
    pipeを認識する
    関数プログラミング(FP)の中でcomposeと似たような方法がpipeです.pipeは、主な役割も複数の関数を組み合わせることです.「ストリーム」と呼ばれます.正常な方法によって、左から右に関数を呼び出します.composeの呼び出し方法とは反対です.
    ES 6 Compse functionを実現
    まずcomposeの基本的な2つのパラメータバージョンを見てください.
    const compose = (f1, f2) => value => f1(f2(value));
    矢印関数を使用して、2つの関数がネストされて実行される関係を非常に直接的に示します.
    次に多層ネストを見ます.
        (f1, f2, f3...) => value => f1(f2(f3));
    抽象的に表示:
         () => () => result;
    まずこれらの基礎的な組み合わせを提案して、私達の後で高級なes 6の方法を理解してcomposeを実現することにとても役立ちます.
    pipeを実現
    前にpipeというのは逆のcomposeで、pipe順方向の呼び出しもより簡単に実現されます.
    pipe = (...fns) => x => fns.reduce((v, f) => f(v), x)
        
    ラインコードはpipeを実現し、上記の抽象的な表現を用いて、reduceはちょうどすべての関数を巡回しています.パラメータxは関数に伝達する初期値として、後のf(v)の度に実行された結果、次のf(v)から呼び出されたパラメータvとして、関数合成呼び出しが完了しました.
    あるいは、関数を組み合わせて、最初の関数がパラメータを取得した後、得られた結果は、reduceエルゴードの初期値とすることができる.
    pipe = (fn,...fns) => (x) => fns.reduce( (v, f) => f(v), fn(x));
    第一の関数fnを抽出し、余分な関数パラメータをfnsに入れると、fnsは配列と見なすことができ、argmentsのように事前にArray.prototype.slice.callを通じて配列に変える必要もなく、性能損失に対しても回避することができる.fn(x)の最初の関数の実行結果はreduceの初期値として行われる.
    compseを実現する
  • pipe部分は、reduceを利用して実現され、逆に見れば、composeはreduceRight
    compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);
    
  • を利用することができます.
  • は再帰的な
    compose = (fn, ...fns) => fns.length === 0 ? fn: (...args) => fn(compose(...fns)(...args))
    再帰コードを利用して、まず輸出条件を見て、fns.length === 0、最後に必ず一番左の関数を実行して、残りの関数をcomposeを通して呼び出します.
  • は、reduceを利用して実現される.具体的な実現コードはここをクリックして、1行で実現します.しかも、正のreduceを使っています.
    const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)))
    の著者らの実用例について説明したが、reduceの反復の方向は左から右へ、composeの実行の方向は右から左へと続く.各実行関数の配列については、通常は(v, f) => f(v)のような実行結果を戻し、f(v)の実行結果を返し、ここでは(f, g) => (...args) => f(g(...args))が関数(...args) => f(g(...args))に戻るので、後の関数gがパラメータとして導入されたときに前の関数fより先に実行されることを保証することができる.前の組み合わせの二つの関数の例を用いて簡単に分析してみます.
    ...
    composeFunc = compose(getFirst, trace, reverse);
    composeFunc(arr);
      
    は主にreduce関数の中の実行過程を見ます.
  • 入口compseFnc(arr)、第1回の反復、reduce関数実行(get First、trace)=>(…args)=>get First(trace(...args))、関数(...args)=>getFirst(trace(...args))は、次の反復における累積器fの値とする.
  • 第二の反復、reduce関数
     f == (...args)=>getFirst(trace(...args))
     g == reverse。
     //      (f, g) => (...args) => f(g(...args))
    ((...args)=>getFirst(trace(...args)), reverse) => (...args) => ((...args)=>getFirst(trace(...args)))(reverse(...args))
    
  • 反復が終わり、最後に得られたcoose Funは
       //           , (...args) => f(g(...args))
    
      (...args) => ((...args)=>getFirst(trace(...args)))(reverse(...args))
  • です.
  • は、関数compseFncを呼び出す.
    (arr) => ((...args)=>getFirst(trace(...args)))(reverse(arr))
    
    ===》reverse(arr)     [3, 2, 1]     
    
     ((...args)=>getFirst(trace(...args)))([3,2,1])
    
    ==》      
    
       getFirst(trace[3,2,1])
    
    ===》 
    
       getFirst([3, 2, 1])
    
    ===》
    
           3
    は、後の関数の実行結果を、前の関数を包む空関数のパラメータとして巧みに伝えて実行します.ここでは、以下の構造
    ((arg)=> f(arg))(arg) 
    //     。
      (function(x) {
         return f(x)
      })(x)
  • が多く使われている.
    最後に
    composeであろうと、後に述べたpipeであろうと、概念は非常に簡単であり、非常に巧妙な方法で実現され(ほとんどreduceを使用している)、しかもプログラミングの中でコードが大幅に簡略化されている.最後に、優秀なフレームにおけるcomposeを使用する例を示します.
  • redux/compose
  • koa-Compse
  • アンダースコア
  • 参照リンク:
  • Creating an ES 6 ish Compse in Javascript
  • compses.js
  • Optimation-killes