ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript関数のカリー化の詳細説明

JavaScript関数のカリー化の詳細説明

黄舟
黄舟オリジナル
2017-02-27 14:29:141564ブラウズ

JavaScript関数カリーリングの詳しい説明

Baidu百科事典のカリーリングの説明: コンピュータサイエンスにおいて、カリーリングとは、複数のパラメーターを受け入れる関数を、単一のパラメーターを受け入れる関数(元の関数、最初の引数) を受け取り、残りの引数を受け入れて結果を返す新しい関数を返します。この手法は論理学者のハスケル・カリーにちなんでクリストファー・ストレイチーによって命名されましたが、発明したのはモーゼス・シュンフィンケルとゴットロブ・フレーゲでした。
上記の説明を読んで、カリー化がどのような関数であるかをすぐに理解できる人は多くないと思います。平たく言えば、関数カリー化の主な目的は、関数パラメータを削減し、いくつかの固定パラメータをプライベート化することです。以下は、関数カリー化の原理を説明するために円の面積を計算するための非常に単純なコードを示しています:

//circle函数,接受半径r和πfunction circle(r,p){
    //计算半径为r的圆的面积
    var area=p*r*r;    return area;
}/*
 * 通过函数柯里化来简化circle函数,只传入半径就能计算出面积
 * 不管怎么样,π是不会变的,因此我们将他写死,不需要外部调用传入
 */function curryCircle(r){
    var p=3.14;    var area=p*r*r;    return area;
}

このコードは奇妙だと思うかもしれませんが、これが関数カリー化の本当の姿です。もちろん、上記のコードは非常に小さな例にすぎません。実際の関数カリー化はもう少し悪質です。以下でより一般的な例について説明します。 π が一意ではないと仮定すると (たとえば、3 種類の π がある)、円の面積を計算する式の π はさまざまなシナリオに応じて変化します。現時点では、それを直接記述することはできませんが、π を構成する必要があります。さまざまな環境に応じて:

//circle函数,接受半径r和π
function circle(r,p){
    //计算半径为r的圆的面积
    var area=p*r*r;    
    return area;
}
//针对circle函数的柯里化函数function curry(fn,p){
    var finalMethod=function(r){
        var result=fn(r,p);        return result;
    }    return finalMethod;
}
//我们有3种不同的πvar curryCircle1=curry(circle,1.14);
var curryCircle2=curry(circle,2.14);
var curryCircle3=curry(circle,3.14);
//输出:4.56  8.56  12.56
console.log(curryCircle1(2),curryCircle2(2),curryCircle3(2));

はい ご覧のとおり、curry メソッドは最も基本的な Circle メソッドをカプセル化し、設定された p パラメータ (π) を保存し、finalMethod メソッドを返すため、最終的に FinalMethod を呼び出すときに必要なのは、パラメータ r (半径) を渡します。関数カリー化の助けを借りて、円の面積を計算するための 3 つの簡素化された方法があります。上に示した関数カリー化は、円の面積の計算にのみ適用できます。今回は、より一般的なカリー化関数を作成します。

function curry(fn){
    //第一个参数是基础执行方法,slice切除
    var args=Array.prototype.slice.call(arguments,1);    //直接返回匿名函数
    return function(){
        //slice新参数以便能调用concat
        var innerArgs=Array.prototype.slice.call(arguments);        //将配置的参数和新传入的参数合并
        var finalArgs=args.concat(innerArgs);        return fn.apply(null,finalArgs);
    };
}

curry() 関数の主な仕事は、返された関数のパラメーターを並べ替えることです。 Curry() の最初のパラメータはカリー化される関数であり、他のパラメータは渡される値です。最初のパラメータ以降のすべてのパラメータを取得するには、引数オブジェクトでslice()メソッドが呼び出され、パラメータ1が渡されて、返された配列に2番目のパラメータ以降のすべてのパラメータが含まれていることを示します。 args 配列には、外部関数からの引数が含まれます。内部関数では、すべての受信パラメータを格納するために innerArgs 配列が作成されます (slice() が再度使用されます)。外部関数と内部関数を格納するパラメーター配列を取得したら、 concat() メソッドを使用してそれらを FinalArgs にマージできます。最後に apply() を使用して結果を関数に渡します。これにより、一般的な関数のカリー化が実現されます。この時点でまだエネルギーが残っている場合は、jQuery で使用される関数のカリー化を紹介します。
メソッドをカリー化するとき、関数をラップするようにカリー化に指示するために fn を渡す必要さえありません。プロトタイプを通じて関数とカリー化を直接バインドできます:

Function.prototype.curry=function(){
    //利用原型的便利,我们可以直接通过this引用到方法
    var fn=this;    var args=Array.prototype.slice.call(arguments);    return function(){
        var arg=0;        //循环校验先前传入的参数和新传入的参数是否有差别
        for(var i=0;i<args.length && arg<arguments.length;i++){            if(args[i]===undefined){
                args[i]=arguments[arg++];
            }
        }        return fn.apply(this,args);
    };
};

前とは異なり、これを通じてメソッド参照を取得します。したがって、関数をカリーにする必要がある場合は、次のように記述するだけで済みます。

var delay=setTimeout.curry(undefined,10);

lay は、事前に 10 ミリ秒の遅延が設定されている setTimeout 関数です。パラメータ設定の保存には引き続き args を使用しますが、今回は違いがあります。args と引数の違いが for ループ内でチェックされ、この判断に基づいてパラメータの結合が完了します。したがって、curry に渡されるパラメータは完全なパラメータである必要があります (つまり、未定義の値を渡す必要があります)。最後に、fn を渡す必要のないカリー化メソッドを実装しました。

JavaScript 関数カリー化の詳細説明

Baidu 百科事典によるカリー化の説明: コンピューターサイエンスにおいて、カリー化とは、複数のパラメーターを受け入れる関数を、単一のパラメーター (元の関数の最初のパラメーター) を受け入れる関数に変換することです。 1 つのパラメータを受け取り、残りのパラメータを受け入れて結果を返す新しい関数を返します。この手法は、モーゼス シュンフィンケルとゴットロブ フレーゲによって発明されましたが、論理学者のハスケル カリーにちなんでクリストファー ストラチーによって命名されました。
上記の説明を読んで、カリー化がどのような関数であるかをすぐに理解できる人は多くないと思います。平たく言えば、関数カリー化の主な目的は、関数パラメータを削減し、いくつかの固定パラメータをプライベート化することです。以下は、関数カリー化の原理を説明するために円の面積を計算するための非常に単純なコードを示しています:

//circle函数,接受半径r和πfunction circle(r,p){
    //计算半径为r的圆的面积
    var area=p*r*r;    return area;
}/*
 * 通过函数柯里化来简化circle函数,只传入半径就能计算出面积
 * 不管怎么样,π是不会变的,因此我们将他写死,不需要外部调用传入
 */function curryCircle(r){
    var p=3.14;    var area=p*r*r;    return area;
}

このコードは奇妙だと思うかもしれませんが、これが関数カリー化の本当の姿です。もちろん、上記のコードは非常に小さな例にすぎません。実際の関数カリー化はもう少し悪質です。以下でより一般的な例について説明します。 π が一意ではないと仮定すると (たとえば、3 種類の π がある)、円の面積を計算する式の π はさまざまなシナリオに応じて変化します。現時点では、それを直接記述することはできませんが、π を構成する必要があります。さまざまな環境に応じて:

//circle函数,接受半径r和πfunction circle(r,p){
    //计算半径为r的圆的面积
    var area=p*r*r;    return area;
}//针对circle函数的柯里化函数function curry(fn,p){
    var finalMethod=function(r){
        var result=fn(r,p);        return result;
    }    return finalMethod;
}
//我们有3种不同的π
var curryCircle1=curry(circle,1.14);
var curryCircle2=curry(circle,2.14);
var curryCircle3=curry(circle,3.14);
//输出:4.56  8.56  12.56
console.log(curryCircle1(2),curryCircle2(2),curryCircle3(2));

可以看到,curry方法通过封装最基础的circle方法,同时保存设置好的p参数(π),并返回一个finalMethod方法,这样我们最终调用finalMethod时就只需要传入参数r(半径)就可以完成。借助函数柯里化,我们拥有了三个简化的计算圆面积方法。上面展示的函数柯里化只能适用于圆面积的计算,这次我们编写一个更通用的柯里化函数:

function curry(fn){
    //第一个参数是基础执行方法,slice切除
    var args=Array.prototype.slice.call(arguments,1);    //直接返回匿名函数
    return function(){
        //slice新参数以便能调用concat
        var innerArgs=Array.prototype.slice.call(arguments);        //将配置的参数和新传入的参数合并
        var finalArgs=args.concat(innerArgs);        return fn.apply(null,finalArgs);
    };
}

curry()函数的主要工作就是将被返回函数的参数进行排序。Curry()的第一个参数是要进行柯里化的函数,其他参数是要传入的值。为了获取第一个参数之后的所有参数,在arguments对象上调用了slice()方法,并传入参数1表示被返回的数组包含从第二个参数开始的所有参数。然后args数组包含了来自外部函数的参数。在内部函数中,创建了innerArgs数组用来存放所有传入的参数(又一次使用了slice())。有了存放来自外部函数和内部函数的参数数组后,就可以使用concat()方法将他们合并成finalArgs。最后使用apply()将结果传递给该函数。这样就实现了一个通用的函数柯里化。如果到这此还有余力的读者可以接着往下看,我们将要介绍jQuery中使用的函数柯里化。
在针对某个方法进行柯里化时,我们甚至不用传入fn来告诉柯里化来包装我们的函数,我们可以通过原型直接将函数和柯里化绑定:

Function.prototype.curry=function(){
    //利用原型的便利,我们可以直接通过this引用到方法
    var fn=this;    var args=Array.prototype.slice.call(arguments);    return function(){
        var arg=0;        //循环校验先前传入的参数和新传入的参数是否有差别
        for(var i=0;i<args.length && arg<arguments.length;i++){            if(args[i]===undefined){
                args[i]=arguments[arg++];
            }
        }        return fn.apply(this,args);
    };
};

与之前不同,我们通过this引用获取了方法引用,这样当我们需要将某个函数柯里化时,只要这样写就可以:

var delay=setTimeout.curry(undefined,10);

delay就是一个已经被提前设定了10毫秒延迟的setTimeout函数。我们仍然通过args来保存参数配置,不过这次有点区别:在for循环内部会校验args和arguments的区别,以此判断来完成参数拼接。所以传给curry的参数必须是完整参数(即意味着不传的值要传入undefined)。最终我们实现了一个不需要传入fn的柯里化方法。

 以上就是详解JavaScript函数柯里化 的内容,更多相关内容请关注PHP中文网(www.php.cn)!


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。