Javascriptキャッシュ関数&コリゼーション&偏関数


キャッシュ関数
memorization
定義:前回の計算結果をキャッシュし、次回呼び出した場合、同じパラメータがあれば、そのままキャッシュのデータに戻ります.
let add = (a,b) => a+b;
let calc = memoize(add);
calc(10,20);//30
calc(10,20);//30   
このような機能を実現するには、主にクローズド、コリゼーション、高次関数に依存します.
実現原理:パラメータと対応する結果データを一つのオブジェクトに存在させ、呼び出し時にパラメータに対応するデータが存在するかどうかを判断し、存在すると対応する結果データを返します.そうでないと計算結果に戻ります.
理論ができました.キャッシュ関数を実現します.
let memoize = function (func, content) {
  let cache = Object.create(null)
  content = content || this
  return (...key) => {
    if (!cache[key]) {
      cache[key] = func.apply(content, key)
    }
    return cache[key]
  }
}
プロセス解析:
  • は、現在の関数領域において、実行結果をキャッシュするための空のオブジェクトを定義しています.
  • はカリン化を用いて関数に戻り、返ってきた関数は、作用ドメインチェーンのためにcache
  • にアクセスできる.
  • は、入力パラメータがcacheの中にあるかどうかを判断する.既に存在する場合は、cacheのコンテンツに直接戻り、存在しない場合は、関数funcを使用して入力パラメータに対して値を求め、結果をcacheに格納する.
  • Vueにも表れています.
    /**
     * Create a cached version of a pure function.
     */
    function cached (fn) {
      var cache = Object.create(null);
      return (function cachedFn (str) {
        var hit = cache[str];
        return hit || (cache[str] = fn(str))
      })
    }
    
    /**
     * Capitalize a string.
     */
    var capitalize = cached(function (str) {
      return str.charAt(0).toUpperCase() + str.slice(1)
    });
    
    ...
    
    capitalize(camelizedId)
    適用シーン:
  • は大量の繰り返し計算が必要です.
  • は大量に計算し、以前の結果に依存する.
    curryと偏関数
    curryfunction curryingは、複数のパラメータを受け取る関数を単一のパラメータを受け入れる関数に変換する.
    //       
    var add = function (x,y) {
        return x+y;
    }
    add(3,4) //7
    
    //      
    var add2 = function (x) {
        //**    **
        return function (y) {
            return x+y;
        }
    }
    add2(3)(4) //7
    上記の例では、多次元パラメータの関数を分割し、最初の関数を受け入れて、その後、次のパラメータを受信するための新しい関数を返します.
    これについて、初歩的な結論を出しました.コリック化後の関数は、形パラメータの数が参個数に等しい場合、関数実行結果を返します.いいえ、コリアン関数を返します.
    コリック化によってコード多重が可能になり、関数を使ってプログラミングされます.
    コリメート関数を実現
    上記の例から、2つのモジュラスがある関数を定義し、コリゼーションを実現するために、関数が最初のモジュラスに入ってきた後、1つの関数を返して第2のモジュラスを受信する.もし私たちの定義のモジュラスが三つあるなら、2層を入れ子し、それぞれ後の2つのパラメータを処理する必要があります.
    var add3 = function (x) {
        return function (y) {
            return function (z) {
                return x + y + z;
            }
        }
    }
    add3(1)(3)(5)
    もし形参が5つあったら、7つは?ここでは再帰を使って簡略化します.規則を見ましたか?形参の個数は関数のネスト層数を決めました.nパラメータがあれば、n-1関数を入れ替える必要があります.
    //       
    function currying (fn) {
        //                https://www.cnblogs.com/go4it/p/9678028.html
        var limit = fn.length; 
        var params = []; //            ,         
        return function _curry(...args) { 
            params = params.concat(args); //       
            if (limit <= params.length) {
                let tempParams=params.slice(0,limit)
                if(limit===params.length){ //               
                    params=[]
                }
                //         
                return fn.apply(null, params);
            } else {
                //          
                return _curry;
            }
        };
    }
    function add(x,y,z){
        return x + y+z;
    }
    //      
    var addCurried=currying(add);
    console.log(`addCurried(1)(2)(3)`,addCurried(1)(2)(3))//6
    console.log(`addCurried(3,3,3)`,addCurried(3,3,3))//9
    console.log(`addCurried(1,2)(3)`,addCurried(1,2)(3))//6
    console.log(`addCurried(3)(4,5)`,addCurried(3)(4,5))//12
    addCurried(1)(2)(3)に何があったか見てみましょう.
  • は、まず`addCurried(1)を呼び出し、1をロケーションに保存し、その後、再帰的に_curryを呼び出して、後続のパラメータ
  • を収集し続ける.
  • addCurried(1)(2)、パラメータ2は、第1回のパラメータ1とマージされて呼び出されたが、外形個数の要件に達していないので、_curry
  • に再帰される.
  • addCurried(1)(2)(3)を呼び出し、3は、次の呼び出しにおいて1,2とマージされ、元の関数add
  • に入力される.
    注意点
  • コリック化は、クローズドパッケージに基づいて実現され、メモリリークを引き起こす可能性がある
  • は再帰的に使用すると、実行は性能を低下させ、再帰的に多くなるとスタックオーバーフローが発生し、再帰的に最適化する必要があり、参照
  • アーグメンントはクラス配列であり、Array.prototype.slice.callを用いて配列に変換する場合、効率が低い.
  • 偏関数
    簡単な説明とは、関数のいくつかのパラメータを先に硬化し、標準値を設定し、新しい関数を返して、残りのパラメータを新しい関数で受信し続けることで、この新しい関数を呼び出すとより簡単になります.
    //   
    let multi = (x,y) => x * y;
    //          2   
    let double = multi.bind(null,2);
    console.log(double(3));//6
    console.log(double(5));//10
    この例では、bindを用いて乗数を固定し、関数を返します.関数は、掛け算としてパラメータを受け取ります.一部のパラメータを固定し、残りのパラメータのみを計算します.
    以上の導出に基づいて、非結合コンテキストの偏り関数を実現した.
    /**
     *      
     * @param func     
     * @param argsBound     
     * @return {function(...[*]): *}
     */
    let partial = (func, ...argsBound) => {
      if (typeof func !== 'function') throw new TypeError(
        `${typeof func} is not a function`)
      return function (...args) { // (*)
        if(func.length-argsBound.length>args.length) throw new Error(`miss arguments`)
        return func.call(this, ...argsBound.concat(...args))
      }
    }
    let partialMulti= partial(multi,2)
    console.log(partialMulti());//Error: miss arguments
    console.log(partialMulti(3));//6
    partial(func[, arg1, arg2...])の呼び出しの結果、funcに基づくパッケージ関数、および:
  • は、入ってきた関数と一致するthis
  • です.
  • は、次いで...argsBoundに入ってくる.偏向関数から着信パラメータ
  • を呼び出す.
  • は次に、...argsに入る.カプセル化関数に入るパラメータ
  • .
    違います
    偏関数はコリックと似ています.これからは比較します.
    コリック化:一つのパラメータ関数を複数の単一パラメータの関数に変換します.つまり、一つのn要素関数をn個の一要素関数に変換します.
    偏関数:関数を固定する1つ以上のパラメータ、すなわちn要素関数を1つのn−x要素関数に変換します.
    個人的な理解:偏関数はカリー化の一種の特定の応用シーンである.
    使用シーン
  • 動的生成関数
  • 減少パラメータ
  • 遅延計算