JavaScriptにおけるPartialアプリとCurrying

10913 ワード

この文章はメモを勉強しています.JSで勉強している知識点と私の理解点、知識点とテクニック自体は私のオリジナルではありません.(引用または参考にした文章の出所は文末)
Partial Appleication(偏関数アプリケーション)とCurrying(ガリガリ化)の字面の意味を説明しないで、実際の例から始めると便利です.
例えば、function sum(a,b){return a+b}があります.
値を固定したいなら、まずこの値を設定します.
var a=1
sum(a,1) sum(a,100)
または新しい関数function newSum(x){return sum(1,x)}を定義します.
固定値を変更するには、複数の新しい関数を定義する必要があります.
または工場の建設方法function makeSum(n){return function(x){return sum(n+x)} var sum 10=makeSum(10);sum 10(1);
しかし、この工場の方法はsumのロジックしか産出できません.どのようなロジックを入力できる工場が必要ですか?
function bindFirstArg(fn,n){//
     return function(x){
          return fn(n,x);
     }
}
  
このように使ってもいいです 
function add(a,b){return a+b}
function del(a,b){return a-b;}
var add 10=bindFirstArg(add,10)、add d 10(1)
var del 10=bindFirstArg(del,10);del 10(1);
 
しかし、変数のパラメータが一つ以上ではなく、個数も一定ではない場合は、どうすればいいですか?
これはいわゆるPartialアプリです.
Partial application can be described as taking a function that accepts some number of arguments, binding values to one or more of those arguments, and returning a new function that only accepts the remaining, un-bound arguments.
簡単に言えば、関数が複数のパラメータの場合、いくつかのパラメータの値を固定したいです.
function partial(fn/*,args…..*/){
     var args=Array.prototype.slice.call(arguments,1);//arguments      , length,     ,   array      ,       this  
     return function(){
          var nargs=Array.prototype.slice.call(arguments,0);//   arguments    , arguments    arguments
          fn.apply(this,args.contact(nargs));
     }
}
コードは複雑ではありません.使い方は以下の通りです.
function sum(){
     var args=Array.prototype.slice.call(arguments,0),n=0;
     for(var i=0;i<args.length;i++){n+=args[i]}
     return i;
}
sum(1,2,3);//    ,  6
var p=partial(sum,10); p(1,2,3);//  16
このような呼び出しが成功したのはsum関数の内部ではargmentsで着信パラメータを判断していますが、書き込みがないためです.sumがデッドパラメータの個数(function sum(a,b){return a+b}を書くと、問題が発生して、着信パラメータより多くは無視されます.
現在のpartialはパラメータを一次的に統合しています.いくつかのパラメータを固定したいなら、次のように変更できます.
var partialAny=(function(){
     var slice=Array.prototype.slice;//                 
     partialAny._={};//            
     return function(fn/*,args…..*/){//  return fn     partial
          var orginArgs=slice.call(arguments,1);//partial     
          return function(){//  return fn        ,           
               var newArgs=slice.call(arguments,0);//
               var args=[];
               for(var i=0;i<orginArgs.length;i++)
               {
                    args[i]=orginArgs[i]===partialAny._?newArgs.shift():orginArgs[i];
               }
              return fn.apply(this,args.contact(newArgs));
     } 
})();
これはちょっと複雑ですが、コメントを見ても分かりやすいです.ここの表示が入れ替わるマーク「.u」はちょっと分かりにくいです.例を見たら分かりやすいです.
function hex(r, g, b) { return '#' + r + g + b; } 
hex('11', '22', '33'); // "#112233" 
var __ = partialAny._; 
var redMax = partialAny(hex, 'ff', __, __); 
redMax('11', '22'); // "#ff1122"
 
次にCurrryingであり、Partial Appleicationと非常に似ていますが、他の問題を解決するために用いられています.複数の単一パラメータ関数で複数のパラメータ関数をどのように実現しますか?
Currying can be described as transforming a function of N arguments in such a way that it can be called as a chain of N functions each with a single argument.
 つまり、パラメータを受信した後、元の関数が必要な個数を満たしているかどうかを判断します.満足しないと、関数に戻ります.足りないパラメータはまだ入力が必要です.十分であれば、直接元の関数を呼び出します.
function curry(fn,n){
     if(typeof n !==’number’){n=fn.length;}//
     function getCurriedFn(prev){
          return function(arg){
               var args=prev.contact(arg);
               if(args.length<n){
                    return getCurriedFn(args);
               }
               else{
                    return fn.apply(this,args);
               }
          }
     }
     return getCurriedFn([]);
}
コード自体も分かりやすく、その使い方は以下の通りです.
var i = 0;
function a(arg1, arg2, arg3) { return ++i + ': ' + arg1 + ', ' + arg2 + ', ' + arg3; } 
// Normal function invocation. 
a('x', 'y', 'z'); // "1: x, y, z" 
a('x', 'y'); // "2: x, y, undefined" 
a('x'); // "3: x, undefined, undefined" 
a(); // "4: undefined, undefined, undefined" 
// Curried function invocation. 
var b = curry(a); 
b(); // `a` not invoked, curried function returned 
b('x'); // `a` not invoked, curried function returned 
b('x')('y'); // `a` not invoked, curried function returned 
b('x')('y')('z'); // "5: x, y, z" 
b('x')('y')(); // "6: x, y, undefined" 
b('x')()(); // "7: x, undefined, undefined" 
b()('y')(); // "8: undefined, y, undefined" 
b()()('z'); // "9: undefined, undefined, z" 
b()()(); // "10: undefined, undefined, undefined”
JS自体は多パラメータ関数をサポートしています.他のいくつかの言語ではHaskellのように、多パラメータ関数はすべて加里化によって実現されます.ここではこのような思想と技術を学習し、理解することができます.学習プログラムの開発には一定の役割があります.
ここは私の少しの勉強ノートです.知識を共有する先輩と大牛たちに感謝します.彼らの文章の住所は以下の通りです.
http://benalman.com/news/2012/09/partial-application-in-javascript/
http://alecb.me/blog/currying-partial-application/