JavaScript カレー:実際の応用は何ですか?
-
02-07-2019 - |
質問
まだカレー作りを覚えていないと思います。私はそれが何をするのか、そしてそれをどのように行うのかを理解しています。使う場面が思いつきません。
JavaScript のどこでカリー化を使用していますか (または、主なライブラリがそれを使用しているのはどこですか)?DOM 操作や一般的なアプリケーション開発の例は歓迎します。
答えの一つ アニメーションについて言及しています。のような機能 slideUp
, fadeIn
要素を引数として受け取り、通常はデフォルトの「アニメーション関数」が組み込まれた高階関数を返すカリー化された関数です。いくつかのデフォルトを使用して上位関数を適用するよりも優れているのはなぜでしょうか?
それを使用する上での欠点はありますか?
リクエストに応じて、JavaScript カリー化に関するいくつかの優れたリソースをここに示します。
- http://www.dustindiaz.com/javascript-curry/
- ダグラス・クロックフォード (2008) JavaScript:良い部分
- http://www.svendtofte.com/code/curried_javascript/(ML に寄り道するため、「ML の短期集中コース」のセクション全体をスキップして、「カリー化された JavaScript の書き方」からやり直してください)
- http://web.archive.org/web/20111217011630/http://blog.morrisjohns.com:80/javascript_closures_for_dummies
- JavaScript クロージャーはどのように機能しますか?
- http://ejohn.org/blog/partial-functions-in-javascript (氏。いつものようにお金を諦めてください)
- http://benalman.com/news/2010/09/partial-application-in-javascript/
コメントに表示されたらさらに追加します。
したがって、回答によれば、カリー化と部分適用は一般に便利なテクニックです。
同じ構成で呼び出して高レベル関数を頻繁に「洗練」する場合は、高レベル関数をカリー化 (または Resig の部分使用) して、シンプルで簡潔なヘルパー メソッドを作成できます。
解決
@ハンク・ゲイ
EmiggensTheMind のコメントへの返答:
そのような例は思いつきません カリー化それ自体は JavaScript で役立ちます。これは、複数の引数を持つ関数呼び出しを、呼び出しごとに 1 つの引数を持つ関数呼び出しのチェーンに変換する手法ですが、JavaScript は 1 つの関数呼び出しで複数の引数をサポートします。
ただし、JavaScript では、そして他のほとんどの実際の言語 (ラムダ計算ではない) では、一般的に部分的なアプリケーションと関連付けられています。ジョン・レシグ もっとよく説明してくれる, ですが、要点は、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" );
他のヒント
ここにあります クロージャを使用する 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))); } }
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);
コールバックが呼び出されるとき、 this
に指差す obj
. 。これは通常コードが短くなるため、イベントの状況やスペースを節約する場合に役立ちます。
カリー化は部分的と似ていますが、カリー化が返す関数は引数を 1 つだけ受け入れるという点が異なります (私が理解している限り)。
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:良い部分」では、彼の無礼な発言ではなく、カリーリングの歴史と実際の使用についていくらか言及していました。それを読んだ後、私は関数型プログラミングを勉強していて、それが由来であることに気づくまで、長い間混乱していました。
もう少し考えた結果、JavaScript でのカリー化には有効な使用例が 1 つあると思います。JavaScript を使用して純粋な関数型プログラミング手法を使用して記述しようとしている場合。ただし、まれな使用例のようです。
ここに例を示します。
ユーザーが何をしているかを確認できるように、JQuery を使用して多数のフィールドを計測しています。コードは次のようになります。
$('#foo').focus(trackActivity);
$('#foo').blur(trackActivity);
$('#bar').focus(trackActivity);
$('#bar').blur(trackActivity);
(JQuery 以外のユーザー向けに、いくつかのフィールドがフォーカスを取得したりフォーカスを失ったりするたびに、trackActivity() 関数を呼び出す必要があると言っています。匿名関数を使用することもできますが、4 回複製する必要があるため、それを取り出して名前を付けました。)
ここで、これらのフィールドの 1 つを別の方法で処理する必要があることがわかりました。これらの呼び出しの 1 つでパラメータを渡して、追跡インフラストラクチャに渡すことができるようにしたいと考えています。カレーならできますよ。
それは魔法でも何でもありません...匿名関数の快適な省略形にすぎません。
partial(alert, "FOO!")
と同等です function(){alert("FOO!");}
partial(Math.max, 0)
に対応 function(x){return Math.max(0, x);}
部分的な (もちキット 用語。他のライブラリの中には、関数に同じことを行う .curry メソッドを提供するものもあると思います) は、匿名関数よりも若干見た目が良く、ノイズも少ないと思います。
それを使用するライブラリに関しては、常に 機能的.
JS でどのような場合に役立ちますか?おそらく他の現代言語でも同じように役立つでしょうが、私がそれを使用しているのを目にすることができるのは、部分的な適用と組み合わせたときだけです。
おそらく、JS のすべてのアニメーション ライブラリはカリー化を使用していると思います。呼び出しごとに、影響を受ける要素のセットと、要素がどのように動作するかを記述する関数を、すべてのタイミングを保証する高次関数に渡す必要があるよりも、パブリック API としていくつかの機能をリリースする方が一般に顧客にとって簡単です。 「slideUp」や「fadeIn」のような関数は要素のみを引数として受け取り、デフォルトの「アニメーション関数」が組み込まれた高階関数を返す単なるカリー化された関数です。
JavaScript 関数は、他の関数型言語では lamda と呼ばれます。これを使用して、別の開発者の単純な入力に基づいて新しい API (より強力な関数または複雑な関数) を作成できます。カレーはテクニックのひとつにすぎません。これを使用して、複雑な API を呼び出すための簡素化された API を作成できます。単純化された API を使用する開発者 (たとえば、簡単な操作を行うために jQuery を使用する場合) の場合は、curry を使用する必要はありません。ただし、簡素化された API を作成したい場合は、カレーが味方です。JavaScript フレームワーク (jQuery、mootools など) またはライブラリを作成する必要があります。そうすれば、その威力を理解できます。強化されたカレー関数を書きました。 http://blog.semanticsworks.com/2011/03/enhanced-curry-method.html 。カリー化を行うためにカリー メソッドを使用する必要はありません。カリー化を行うのに役立つだけですが、別の関数 B(){} を返す関数 A(){} を記述することで、いつでも手動で行うことができます。さらに面白くするには、関数 B() を使用して別の関数 C() を返します。
私はその古いスレッドを知っていますが、これが 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」を置き換えるために使用されるコンテキストをバインドできます。バインドされていない場合、関数のデフォルトはウィンドウ スコープになります。
アドバイス:車輪を再発明する必要はありません。部分的な適用/バインディング/カリーは非常に関連しています。上記の違いがわかります。この意味をどこでも使用すると、人々はあなたが何をしているのかを問題なく理解できるようになり、使用するコードも少なくなります。
最初の引数の値が常に入力される疑似関数を作成して、物事を進めたい場合があることに私も同意します。幸いなことに、私は jPaq というまったく新しい JavaScript ライブラリに出会いました (http://jpaq.org/) この機能を提供します。このライブラリの最も優れた点は、必要なコードのみを含む独自のビルドをダウンロードできることです。
私は、curry 関数のいくつかの優れたアプリケーションを示す jPaq の例を書きました。ここで確認してください: 文字列関数のカレー化
Functional.js のリソースを追加したいだけです。
いくつかのアプリケーションを説明する講演会/カンファレンスhttp://www.youtube.com/watch?v=HAcN3JyQoyY
更新された Functional.js ライブラリ:https://github.com/loop-recur/FunctionalJSいくつかの素晴らしいヘルパー (申し訳ありませんが、ここでは初めてです。評判がありません :p):/loop-recur/PreludeJS
私は最近、js IRC クライアント ヘルパー ライブラリでの繰り返しを減らすためにこのライブラリをよく使用しています。これは素晴らしいもので、コードをクリーンアップして簡素化するのに非常に役立ちます。
さらに、パフォーマンスが問題になる場合 (ただし、このライブラリは非常に軽い)、ネイティブ関数を使用して書き直すだけで簡単です。
ネイティブ バインドを使用すると、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
もう一つの取り組みは、Promise の扱いからです。
(免責事項:Python の世界から来た、JS 初心者。そこでさえ、 カリー化 あまり使用されませんが、場合によっては便利です。そこで私はカリー化関数を作成しました - リンクを参照してください)
まず、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