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


172

私はまだカレーを食べたことはないと思います。私はそれが何をするか、そしてどのようにそれを行うかを理解しています。自分が使う状況なんて考えられない。

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

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

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

ここで要求されているように、JavaScriptカレーに関するいくつかの優れたリソースがあります。

コメントに表示されるので、さらに追加します。


そのため、回答によれば、カレーや部分塗布は一般的に便利なテクニックです。

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


JSカレーとは何かを説明するリソースへのリンクを追加できますか?チュートリアルやブログ投稿がいいです。
Eric Sc​​hoonover、

2
svendtofte.comは長続きしますが、「MLのクラッシュコース」のセクション全体をスキップして、「カリー化されたJavaScriptの記述方法」から再度開始すると、jsでのカリー化の素晴らしい紹介になります。
danio

1
これは、カレーと部分的なアプリケーションが実際に何であるかを理解するための良い出発点です:slid.es/gsklee/functional-programming-in-5-minutes
gsklee '27

1
リンクsvendtofte.com死者になりそうだが-にかかわらず、ウェイバックマシン上でそれを見つけたweb.archive.org/web/20130616230053/http://www.svendtofte.com/...申し訳ありませんが、blog.morrisjohns.com/javascript_closures_for_dummiesがダウンしているようですあまりにも
phatskat 2014年

1
ところで、Resigのパーシャルのバージョンは、事前に初期化された(「カリー化された」)引数の1つにundefinedの値が指定されていると失敗する可能性があるという点で不十分です(「オンザマネー」ではありません)。優れたカレー機能に興味がある人は、オリバースティールのfuncitonal.jsからオリジナルを入手する必要があります。問題はありません。
RobG 2014

回答:


35

@ハンクゲイ

EmbiggensTheMindのコメントに応じて:

カレー自体がJavaScriptで役立つ例は考えられません。これは、複数の引数を持つ関数呼び出しを、呼び出しごとに1つの引数を持つ関数呼び出しのチェーンに変換する手法ですが、JavaScriptは1つの関数呼び出しで複数の引数をサポートしています。

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

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

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


6
これは本当に悪い答えです。カリー化は部分適用とは何の関係もありません。カリー化は機能構成を可能にします。関数合成は関数の再利用を可能にします。関数の再利用により、コードの保守性が向上します。とても簡単です!

2
@ftor先生、あなたは非常に悪い答えです。カレーは明らかに機能をより美味しくすることです。あなたは明らかにポイントを逃した。
Callat

112

クロージャーを使用する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"

これはのcurry拡張に依存してFunctionいます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)));
    }
}

5
これは素晴らしい!「Lispはプログラム可能なプログラミング言語である」と言うlispの引用に似ていると思います
サンティアゴバスルト

2
興味深いですが、この例は機能していないようです。offset+inputなりますundefined + 1.60936あなたにmilesToKm例。結果でそれNaN
ネイサンロング、

3
@Nathan-オフセットを未定義にすることはできません-デフォルトは0です
AngusC '10

6
私が読んだもの(今のところ)から、Prototypeライブラリーを使用するか自分で追加しない限り、「カレー」は通常、関数のトリックのバッグの一部ではありません。しかし、とてもクールです。
Roboprog、2012年

11
ES5のbind()メソッドでも同じことができます。Bindは、呼び出されたときに、最初の引数のコンテキストと後続の引数のシーケンス(新しい関数に渡される前)で元の関数を呼び出す新しい関数を作成します。だからあなたはできる... var milesToKm = converter.bind(this、 'km'、1.60936); またはvar farenheitToCelsius = converter.bind(this、 'degrees C'、0.5556、-32); 最初の引数、コンテキスト、これはここでは無関係なので、undefinedを渡すだけでかまいません。もちろん、ES5以外のフォールバックのために独自のバインドメソッドを使用して、基本のFunctionプロトタイプを拡張する必要があります
hacklikecrack

7

私はpythonに似た関数をfunctools.partialJavaScriptでより便利に見つけました:

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);

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

カリー化はパーシャルに似ていますが、カリー化が返す関数は1つの引数しか受け入れない点が異なります(私が理解している限り)。


7

Hank Gayに同意する-必要な部分であるため、特定の真の関数型プログラミング言語で非常に役立ちます。たとえば、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を使用した純粋な関数型プログラミング手法を使用して記述しようとしている場合です。ただし、まれなユースケースのようです。


2
あなたのコードはPrisoner Zeroのコードよりもはるかに理解しやすく、カレーや複雑なものを使わずに同じ問題を解決します。あなたは2つの親指を持っていて、彼はほぼ100を持っています。
DR01D 2017

2

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

partial(alert, "FOO!") に相当 function(){alert("FOO!");}

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

部分的な呼び出し(MochiKit用語。他のいくつかのライブラリは、同じことを行う.curryメソッドを関数に提供していると思います)は、無名関数よりも少し見栄えが良く、ノイズが少ないです。


1

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

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


ありがとうハンク-それが一般的に役に立つとき、あなたは拡張できますか?
デイブノーラン

1

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


いくつかのデフォルトで単に呼び出すよりも、higherup関数をカレーした方がよいのはなぜですか?
Dave Nolan

1
それは、より高い機能がサポートできるすべての「デフォルト」を想像するよりも、希望に応じて「doMathOperation」を追加/乗算/平方/係数/その他の計算でカリー化できるようにするためのモジュール性が非常に高いためです。
ギズモ

1

ここに例があります。

ユーザーの現在の状況を確認できるように、JQueryを使用して一連のフィールドを計測しています。コードは次のようになります。

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

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

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


1

JavaScript関数は、他の関数型言語ではラムダと呼ばれます。別の開発者の単純な入力に基づいて新しい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

私はその古いスレッドを知っていますが、これが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」を置き換えるために使用されるコンテキストをバインドできます。バインドされていない場合、関数のデフォルトはウィンドウスコープになります。

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


0

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



0

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

一部のアプリケーションを説明するレクチャー/会議 http://www.youtube.com/watch?v=HAcN3JyQoyY

更新されたFunctional.jsライブラリ:https : //github.com/loop-recur/FunctionalJS いくつかの優れたヘルパー(ここでは申し訳ありませんが、評判はありません:p):/ loop-recur / PreludeJS

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

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


0

ネイティブバインドを使用して、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

約束を処理することから、それをもう一度試してみてください。

(免責事項:JS noob、Pythonの世界から来ています。カレーはあまり使用されていませんが、時々重宝します。カリー機能を用意しました-リンクを参照してください)

まず、ajax呼び出しから始めます。私は成功を行うために処理するいくつかの特定のを持っていますが、失敗した場合に、私はちょうど呼び出すことをユーザにフィードバックを与えたいと思う何かがもたらしたいくつかのエラー。実際のコードでは、ブートストラップパネルにエラーフィードバックを表示していますが、ここではロギングを使用しています。

これが失敗するようにライブ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);
}

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

私はまだコーディング時にしか情報を利用できません-私の場合、いくつかの可能なバッチがありますが、失敗した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

ここで、少し変更して、再利用可能な汎用エラーハンドラーを使用しますが、実行時にコード時刻がわかっている呼び出しコンテキストとイベントから取得できるランタイム情報の両方でカリー化されるハンドラーも使用します。

    ... 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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.