web-dev-qa-db-ja.com

JavaScriptカレー:実用的なアプリケーションは何ですか?

私はまだカレーを手探りしたとは思わない。私はそれが何をするのか、そしてそれをどのように行うのかを理解しています。私はそれを使用する状況を考えることができません。

JavaScriptのカリーをどこで使用していますか(または、それを使用するメインライブラリはどこですか)。 DOM操作または一般的なアプリケーション開発の例は歓迎します。

答えの1つ アニメーションに言及しています。 slideUpfadeInなどの関数は、引数として要素を取り、通常はデフォルトの「アニメーション関数」が組み込まれた高次関数を返すカリー化された関数です。なぜいくつかのデフォルトで上位関数を適用するよりも優れているのですか?

それを使用することに欠点はありますか?

ここにリクエストされているのは、JavaScriptカリー化に関する優れたリソースです。

コメントに追加されたら追加します。


そのため、答えによると、一般にカレーと部分適用は便利なテクニックです。

高レベル関数を同じ構成で呼び出すことで頻繁に「洗練」する場合は、高レベル関数をカリー化(またはResigのパーシャルを使用)して、シンプルで簡潔なヘルパーメソッドを作成できます。

170
Dave Nolan

@ハンクゲイ

EmbiggensTheMindのコメントへの回答:

currying —それ自体がJavaScriptで有用なインスタンスは考えられません。これは、複数の引数を持つ関数呼び出しを呼び出しごとに1つの引数を持つ関数呼び出しのチェーンに変換する手法ですが、JavaScriptは1つの関数呼び出しで複数の引数をサポートします。

JavaScriptでは(ラムダ計算ではなく)他のほとんどの実際の言語を想定していますが、一般的には部分的なアプリケーションに関連付けられています。 John Resig より良い説明 、しかし要点は、2つ以上の引数に適用されるいくつかのロジックがあり、それらの引数のいくつかの値のみを知っているということです。

部分的なアプリケーション/カリーを使用して、これらの既知の値を修正し、不明な値のみを受け入れる関数を返すことができます。この関数は、後で渡したい値が実際にあるときに呼び出されます。これは、同じJavaScriptビルトインを1つを除いてすべて同じ値で繰り返し呼び出していた場合に、繰り返しを避けるための素晴らしい方法を提供します。ジョンの例を盗むには:

String.prototype.csv = String.prototype.split.partial(/,\s*/);
var results = "John, Resig, Boston".csv();
alert( (results[1] == "Resig") + " The text values were split properly" );
33
Hank Gay

興味深い、そしてクロージャーを使用するJavaScriptでのカリー化の実際的な使用法です

function converter(toUnit, factor, offset, input) {
    offset = offset || 0;
    return [((offset + input) * factor).toFixed(2), toUnit].join(" ");
}

var milesToKm = converter.curry('km', 1.60936, undefined);
var poundsToKg = converter.curry('kg', 0.45460, undefined);
var farenheitToCelsius = converter.curry('degrees C', 0.5556, -32);

milesToKm(10);            // returns "16.09 km"
poundsToKg(2.5);          // returns "1.14 kg"
farenheitToCelsius(98);   // returns "36.67 degrees C"

これはcurryFunction拡張に依存していますが、見てわかるように、applyのみを使用します(派手すぎない):

Function.prototype.curry = function() {
    if (arguments.length < 1) {
        return this; //nothing to curry with - return function
    }
    var __method = this;
    var args = toArray(arguments);
    return function() {
        return __method.apply(this, args.concat([].slice.apply(null, arguments)));
    }
}
112
Prisoner ZERO

Pythonのfunctools.partialに似た関数がJavaScriptでより便利であることがわかりました。

function partial(fn) {
  return partialWithScope.apply(this,
    Array.prototype.concat.apply([fn, this],
      Array.prototype.slice.call(arguments, 1)));
}

function partialWithScope(fn, scope) {
  var args = Array.prototype.slice.call(arguments, 2);
  return function() {
    return fn.apply(scope, Array.prototype.concat.apply(args, arguments));
  };
}

なぜそれを使用したいのですか?これを使用する一般的な状況は、関数のthisを値にバインドする場合です。

var callback = partialWithScope(Object.function, obj);

コールバックが呼び出されると、thisobjを指します。これは通常、コードを短くするため、イベントの状況やスペースを節約するのに役立ちます。

カリー化はパーシャルに似ていますが、カリー化が返す関数が引数を1つだけ受け入れるという違いがあります(私が理解している限り)。

7
Armin Ronacher

ハンクゲイに同意する-それは特定の真の関数型プログラミング言語で非常に役立ちます-それは必要な部分だからです。たとえば、Haskellでは、関数に複数のパラメーターを渡すことはできません-純粋な関数型プログラミングではできません。一度に1つのパラメーターを取り、関数を構築します。 JavaScriptでは、「コンバータ」などの不自然な例にもかかわらず、単に不要です。カリー化する必要のない同じコンバーターコードを次に示します。

var converter = function(ratio, symbol, input) {
    return (input*ratio).toFixed(2) + " " + symbol;
}

var kilosToPoundsRatio = 2.2;
var litersToUKPintsRatio = 1.75;
var litersToUSPintsRatio = 1.98;
var milesToKilometersRatio = 1.62;

converter(kilosToPoundsRatio, "lbs", 4); //8.80 lbs
converter(litersToUKPintsRatio, "imperial pints", 2.4); //4.20 imperial pints
converter(litersToUSPintsRatio, "US pints", 2.4); //4.75 US pints
converter(milesToKilometersRatio, "km", 34); //55.08 km

"JavaScript:The Good Parts"のDouglas Crockfordが、彼の気まぐれな発言ではなく、カレーの歴史と実際の使用について言及してくれたことを強く望みます。それを読んだ後、最も長い間、私は関数型プログラミングを勉強し、それがどこから来たかに気付くまで、私はうろたえました。

さらに考えてみると、JavaScriptをカリー化するための有効な使用例が1つあると仮定します。JavaScriptを使用して純粋な関数型プログラミング手法を使用して作成しようとしている場合です。しかし、まれなユースケースのようです。

5
Byron Katz

以下に例を示します。

JQueryを使用して多数のフィールドをインスツルメントし、ユーザーが何をしているかを確認します。コードは次のようになります。

$('#foo').focus(trackActivity);
$('#foo').blur(trackActivity);
$('#bar').focus(trackActivity);
$('#bar').blur(trackActivity);

(JQuery以外のユーザーの場合、いくつかのフィールドがフォーカスを取得または失うたびに、trackActivity()関数を呼び出したいと言っています。匿名関数も使用できますが、複製する必要があります。 4回なので、引き出して名前を付けました。)

ここで、これらのフィールドの1つを異なる方法で処理する必要があることがわかりました。これらの呼び出しの1つでパラメーターを渡して、追跡インフラストラクチャーに渡すことができるようにしたいと思います。カレーでできます。

2
William Pietri

それは魔法でも何でもありません...匿名関数のための心地よい速記です。

partial(alert, "FOO!")function(){alert("FOO!");}と同等です

partial(Math.max, 0)function(x){return Math.max(0, x);}に対応します

パーシャルへの呼び出し( MochiKit 用語。他のいくつかのライブラリは、関数に同じことを行う.curryメソッドを与えると思います)は、匿名関数よりも少し見栄えが良く、ノイズが少ないように見えます。

2
Marijn

JavaScript関数は、他の関数型言語ではlamdaと呼ばれます。別の開発者の単純な入力に基づいて、新しいAPI(より強力な機能または複雑な機能)を作成するために使用できます。カレーはテクニックの1つにすぎません。これを使用して、複雑なAPIを呼び出す単純化されたAPIを作成できます。単純化されたAPIを使用する開発者であれば(たとえば、jQueryを使用して簡単な操作を行う場合)、カレーを使用する必要はありません。ただし、簡易APIを作成する場合は、カレーがあなたの友人です。 javascriptフレームワーク(jQuery、mootoolsなど)またはライブラリを作成する必要があり、そのパワーを評価できます。 http://blog.semanticsworks.com/2011/03/enhanced-curry-method.html で拡張カレー関数を作成しました。カレーを実行するためにカレーメソッドを使用する必要はありません。カレーを実行するのに役立ちますが、関数A(){}を記述して別の関数B(){}を返すことでいつでも手動で実行できます。さらに面白くするには、関数B()を使用して別の関数C()を返します。

1
Fred Yang

私はその古いスレッドを知っていますが、これがjavascriptライブラリでどのように使用されているかを示す必要があります。

Lodash.jsライブラリーを使用して、これらの概念を具体的に説明します。

例:

var fn = function(a,b,c){ 
return a+b+c+(this.greet || ‘'); 
}

部分適用:

var partialFnA = _.partial(fn, 1,3);

カレー:

var curriedFn = _.curry(fn);

バインド:

var boundFn = _.bind(fn,object,1,3 );//object= {greet: ’!'}

使用法:

curriedFn(1)(3)(5); // gives 9 
or 
curriedFn(1,3)(5); // gives 9 
or 
curriedFn(1)(_,3)(2); //gives 9


partialFnA(5); //gives 9

boundFn(5); //gives 9!

差:

カリー化後、パラメーターが事前にバインドされていない新しい関数を取得します。

部分的に適用した後、いくつかのパラメーターが事前にバインドされた関数を取得します。

バインドでは、「this」を置き換えるために使用されるコンテキストをバインドできます。バインドされていない場合、どの関数のデフォルトもウィンドウスコープになります。

アドバイス:車輪を再発明する必要はありません。部分的なアプリケーション/バインディング/カリー化は非常に関連しています。上記の違いを見ることができます。どこでもこの意味を使用すると、人々は理解している問題なくあなたがしていることを認識し、より少ないコードを使用する必要があります。

1
Shishir Arora

それを使用するライブラリに関しては、常に Functional があります。

JSで役に立つのはいつですか?おそらく他の現代の言語でも同じくらい便利ですが、それを使用しているのが見えるのは部分的なアプリケーションとの組み合わせだけです。

1
Hank Gay

おそらく、JSのすべてのアニメーションライブラリはカリー化を使用していると思います。呼び出しごとに、影響を受ける要素のセットと要素をどのように動作させるかを説明する関数を、すべてのタイミングを保証する高次関数に渡す必要はなく、一般に公開API 「slideUp」、「fadeIn」のような関数で、引数として要素のみを取ります。これらは、デフォルトの「アニメーション関数」が組み込まれた高次関数を返すカリー関数です。

1
gizmo

Functional.jsのリソースをいくつか追加したいだけです。

いくつかのアプリケーションを説明する講義/会議 http://www.youtube.com/watch?v=HAcN3JyQoyY

更新されたFunctional.jsライブラリ: https://github.com/loop-recur/FunctionalJS いくつかの素晴らしいヘルパー(ここでは新しい、評価なし:p):/ loop-recur/PreludeJS

最近、js IRCクライアントヘルパーライブラリでの繰り返しを減らすために、このライブラリを使用しています。これは素晴らしいものです。コードのクリーンアップと単純化に役立ちます。

さらに、パフォーマンスが問題になる場合(ただし、このライブラリはかなり軽い)、ネイティブ関数を使用して簡単に書き換えることができます。

0
megawac

ネイティブバインドを使用して、迅速な1行のソリューションを実現できます。

function clampAngle(min, max, angle) {
    var result, delta;
    delta = max - min;
    result = (angle - min) % delta;
    if (result < 0) {
        result += delta;
    }
    return min + result;
};

var clamp0To360 = clampAngle.bind(null, 0, 360);

console.log(clamp0To360(405)) // 45
0
cstuncsik

約束を処理することから別の突き刺します。

(免責事項:JS noob、Python世界から来ています。そこでも、curryingあまり使用されていませんが、時々便利になることがあります。そのため、カリー化機能を廃止しました-リンクを参照してください)

まず、ajax呼び出しから始めます。成功時に特定の処理を行う必要がありますが、失敗時にはsomethingを呼び出してsome error。実際のコードでは、bootstrapパネルにエラーフィードバックを表示していますが、ここではロギングを使用しています。

これが失敗するようにライブURLを変更しました。

_function ajax_batch(e){
    var url = $(e.target).data("url");

    //induce error
    url = "x" + url;

    var promise_details = $.ajax(
        url,
        {
            headers: { Accept : "application/json" },
            // accepts : "application/json",
            beforeSend: function (request) {
                if (!this.crossDomain) {
                    request.setRequestHeader("X-CSRFToken", csrf_token);
                }
        },
        dataType : "json",
        type : "POST"}
    );
    promise_details.then(notify_batch_success, fail_status_specific_to_batch);
}
_

さて、ここでバッチが失敗したことをユーザーに伝えるために、エラーハンドラーにその情報を書き込む必要があります。取得するのはサーバーからの応答だけだからです。

私はまだコーディング時に利用可能な情報しか持っていません-私の場合、私はいくつかの可能なバッチを持っていますが、どのバッチがw.oに失敗したのか分かりません失敗したURLに関するサーバーの応答を解析します。

_function fail_status_specific_to_batch(d){
    console.log("bad batch run, dude");
    console.log("response.status:" + d.status);
}
_

やってみましょう。コンソール出力は次のとおりです。

コンソール:

bad batch run, dude utility.js (line 109) response.status:404

さて、少し変更して、再利用可能な汎用障害ハンドラーを使用しますが、実行時にcurriedであり、コード時既知の呼び出しコンテキストと実行の両方を使用します-イベントから利用可能な時間情報。

_    ... rest is as before...
    var target = $(e.target).text();
    var context = {"user_msg": "bad batch run, dude.  you were calling :" + target};
    var contexted_fail_notification = curry(generic_fail, context); 

    promise_details.then(notify_batch_success, contexted_fail_notification);
}

function generic_fail(context, d){
    console.log(context);
    console.log("response.status:" + d.status);
}

function curry(fn) {
     var slice = Array.prototype.slice,
        stored_args = slice.call(arguments, 1);
     return function () {
        var new_args = slice.call(arguments),
              args = stored_args.concat(new_args);
        return fn.apply(null, args);
     };
}
_

コンソール:

Object { user_msg="bad batch run, dude. you were calling :Run ACL now"} utility.js (line 117) response.status:404 utility.js (line 118)

より一般的には、JSでコールバックが広く使用されていることを考えると、カリー化は非常に便利なツールのようです。

https://javascriptweblog.wordpress.com/2010/04/05/curry-cooking-up-tastier-functions/http://www.drdobbs.com/open-source/currying-and-partial-functions-in-javasc/231001821?pgno = 2

0
JL Peyret

カリー関数のいくつかのクールなアプリケーションを示すjPaqの例を作成しました。ここで確認してください: 文字列関数の更新

0
Chris West

私は、最初の引数の値を常に入力する疑似関数を作成することにより、ボールを転がしたいということに同意します。幸いなことに、jPaq(h ttp://jpaq.org/ )この機能を提供します。このライブラリの最も良い点は、必要なコードのみを含む独自のビルドをダウンロードできるという事実です。

0