私はいくつかの記事やブログでカレー関数への言及を見てきましたが、良い説明(または少なくとも意味のあるもの)を見つけることができません。
add x y = x+y
(カレー)に異なっているadd (x, y)=x+y
(uncurried)
私はいくつかの記事やブログでカレー関数への言及を見てきましたが、良い説明(または少なくとも意味のあるもの)を見つけることができません。
add x y = x+y
(カレー)に異なっているadd (x, y)=x+y
(uncurried)
回答:
カリー化とは、複数の引数を取る関数を、それぞれが1つの引数のみを取る一連の関数に分解することです。JavaScriptの例を次に示します。
function add (a, b) {
return a + b;
}
add(3, 4); // returns 7
これは、aとbの2つの引数を取り、それらの合計を返す関数です。この関数をカレーします。
function add (a) {
return function (b) {
return a + b;
}
}
これは、1つの引数aを取り、別の引数bを取り、その合計を返す関数を返す関数です。
add(3)(4);
var add3 = add(3);
add3(4);
最初のステートメントは、add(3、4)ステートメントと同様に7を返します。2番目のステートメントは、引数に3を追加するadd3という新しい関数を定義します。これは、一部の人々がクロージャーと呼ぶかもしれないものです。3番目のステートメントでは、add3演算を使用して3に4を加算し、結果として7を生成します。
[1, 2, 3, 4, 5]
、任意の数値を掛けたい数値のリストがあるとします。Haskellでは、map (* 5) [1, 2, 3, 4, 5]
リスト全体をで乗算して5
、リストを生成するように書くことができます[5, 10, 15, 20, 25]
。
map
は、1つの引数(リストからの要素)のみを取る関数でなければなりません。乗算-数学的概念として-は二項演算です。2つの引数を取ります。ただし、Haskellに*
はカリー関数がありadd
、この回答の2番目のバージョンと同様です。の結果(* 5)
は、単一の引数を取り、それを5倍する関数であり、mapで使用できます。
関数の代数では、複数の引数(またはNタプルである同等の1つの引数)をとる関数の処理はやや洗練されていません。 1つの引数を取る関数が必要です。
では、自然に表現したいことにどう対処するのでしょうf(x,y)
か?まあ、あなたはそれをf(x)(y)
- と同等であると見なし、それをf(x)
呼び出しg
、関数であり、その関数をに適用しますy
。つまり、1つの引数を取る関数しかありませんが、それらの関数の一部は他の関数を返します(これらも1つの引数を取ります;-)。
いつものように、ウィキペディアにはこれに関する素晴らしい要約エントリがあり、多くの便利なポインタ(おそらくお気に入りの言語に関するものを含む)と、やや厳密な数学的処理があります。
div :: Integral a => a -> a -> a
-複数の矢印に注意してください?「aをaにマッピングする関数にマップする」は、1つの読みです;-)。&cに(単一の)タプル引数を使用することもできますdiv
が、Haskellではこれは本当に慣用的ではありません。
具体的な例を次に示します。
オブジェクトに作用する重力を計算する関数があるとします。数式がわからない場合は、こちらで確認できます。この関数は、必要な3つのパラメーターを引数として受け取ります。
今、地球上にいるので、この惑星上のオブジェクトの力を計算したいだけです。関数型言語では、地球の質量を関数に渡し、部分的に評価することができます。返されるのは、引数を2つだけ取り、地球上の物体の重力を計算する別の関数です。これをカレーといいます。
カリー化は、関数に適用できる変換であり、以前よりも引数を1つ少なく取ることができます。
たとえば、F#では次のように関数を定義できます。
let f x y z = x + y + z
ここで、関数fはパラメーターx、y、zを受け取り、それらを合計します。
f 1 2 3
6を返します。
したがって、この定義から、fのカレー関数を定義できます。
let curry f = fun x -> f x
ここで、 'fun x-> fx'は、C#のx => f(x)と同等のラムダ関数です。この関数は、カレーしたい関数を入力し、単一の引数を取り、最初の引数を入力引数に設定して指定された関数を返す関数を返します。
前の例を使用すると、fのカレーを取得できます。
let curryf = curry f
その後、次のことを実行できます。
let f1 = curryf 1
これは、f1 yz = 1 + y + zと同等の関数f1を提供します。これは、以下を実行できることを意味します。
f1 2 3
これは6を返します。
このプロセスは、以下のように定義できる「部分機能アプリケーション」と混同されることがよくあります。
let papply f x = f x
それを複数のパラメータに拡張できますが、つまり:-
let papply2 f x y = f x y
let papply3 f x y z = f x y z
etc.
部分的なアプリケーションは関数とパラメーターを受け取り、1つ以上のパラメーターを必要としない関数を返します。前の2つの例が示すように、標準のF#関数定義に直接実装されているため、前の結果を達成できます。
let f1 = f 1
f1 2 3
これは6の結果を返します。
結論として:-
カレーと部分関数の適用の違いは次のとおりです。
カリー化は、関数を受け取り、単一の引数を受け入れ、最初の引数をその引数に設定して、指定された関数を返す新しい関数を提供します。これにより、複数のパラメーターを持つ関数を一連の単一引数関数として表すことができます。例:-
let f x y z = x + y + z
let curryf = curry f
let f1 = curryf 1
let f2 = curryf 2
f1 2 3
6
f2 1 3
6
部分関数の適用はより直接的です-関数と1つ以上の引数を取り、最初のn個の引数が指定されたn個の引数に設定された関数を返します。例:-
let f x y z = x + y + z
let f1 = f 1
let f2 = f 2
f1 2 3
6
f2 1 3
6
関数を使用して他の関数を作成する方法にもなります。
JavaScriptで:
let add = function(x){
return function(y){
return x + y
};
};
次のように呼び出すことができます:
let addTen = add(10);
これが実行される10
と、として渡されx
ます。
let add = function(10){
return function(y){
return 10 + y
};
};
つまり、この関数が返されます。
function(y) { return 10 + y };
だからあなたが呼び出すとき
addTen();
あなたは本当に電話しています:
function(y) { return 10 + y };
したがって、これを行う場合:
addTen(4)
それは同じです:
function(4) { return 10 + 4} // 14
したがって、addTen()
渡したものには常に10が加算されます。同様の関数を同じ方法で作成できます。
let addTwo = add(2) // addTwo(); will add two to whatever you pass in
let addSeventy = add(70) // ... and so on...
明らかなフォローアップの質問ですが、なぜ地球上でそれをしたいのでしょうか?それは熱心な操作だったものを変えますx + y
遅延して実行できる変換します。つまり、少なくとも2つのことを実行できます。1.負荷の高い操作をキャッシュします。
カレー関数が次のようになっているとします。
let doTheHardStuff = function(x) {
let z = doSomethingComputationallyExpensive(x)
return function (y){
z + y
}
}
この関数を1回呼び出してから、結果を渡して多くの場所で使用することができます。つまり、計算に負荷のかかる処理を1回だけ実行します。
let finishTheJob = doTheHardStuff(10)
finishTheJob(20)
finishTheJob(30)
同様の方法で抽象化を取得できます。
カリー化された関数は、最初の引数を受け入れ、2番目の引数を受け入れる関数を返すように書き換えられたいくつかの引数の関数です。これにより、いくつかの引数の関数に、初期引数の一部を部分的に適用できます。
map
関数f
を使用するxss
場合は、次のようにできますmap (map f) xss
。
Pythonのおもちゃの例を次に示します。
>>> from functools import partial as curry
>>> # Original function taking three parameters:
>>> def display_quote(who, subject, quote):
print who, 'said regarding', subject + ':'
print '"' + quote + '"'
>>> display_quote("hoohoo", "functional languages",
"I like Erlang, not sure yet about Haskell.")
hoohoo said regarding functional languages:
"I like Erlang, not sure yet about Haskell."
>>> # Let's curry the function to get another that always quotes Alex...
>>> am_quote = curry(display_quote, "Alex Martelli")
>>> am_quote("currying", "As usual, wikipedia has a nice summary...")
Alex Martelli said regarding currying:
"As usual, wikipedia has a nice summary..."
(Python以外のプログラマが気を散らすのを避けるために、+による連結を使用するだけです。)
追加する編集:
http://docs.python.org/library/functools.html?highlight=partial#functools.partialを参照してください。これは、Pythonがこれを実装する方法における部分オブジェクトと関数の違いも示しています。
カリーとは、関数をcallable asからcallable as f(a, b, c)
に変換することf(a)(b)(c)
です。
それ以外の場合、カリー化とは、複数の引数をとる関数を、引数の一部をとる一連の関数に分解することです。
文字通り、カリー化は関数の変換です。ある方法から別の方法で呼び出します。JavaScriptでは、通常、元の関数を保持するラッパーを作成します。
カリー化は関数を呼び出しません。変身するだけです。
引数が2つの関数をカリー化するカリー関数を作ってみましょう。つまり、curry(f)
2つの引数があるため、次のようにf(a, b)
変換されます。f(a)(b)
function curry(f) { // curry(f) does the currying transform
return function(a) {
return function(b) {
return f(a, b);
};
};
}
// usage
function sum(a, b) {
return a + b;
}
let carriedSum = curry(sum);
alert( carriedSum(1)(2) ); // 3
ご覧のとおり、実装は一連のラッパーです。
curry(func)
はラッパーfunction(a)
です。sum(1)
に呼び出されると、引数は字句解析環境に保存され、新しいラッパーが返されfunction(b)
ます。sum(1)(2)
最終的にfunction(b)
提供2を呼び出し、元の複数引数の合計に呼び出しを渡します。あなたが理解しpartial
ているなら、あなたはそこに半分いる。のアイデアpartial
、関数に引数を事前に適用し、残りの引数のみを必要とする新しい関数を返すことです。この新しい関数が呼び出されると、プリロードされた引数と、それに提供された引数が含まれます。
Clojure +
では関数ですが、物事を明確にするために:
(defn add [a b] (+ a b))
このinc
関数は、渡された数値に1を追加するだけであることに気づくかもしれません。
(inc 7) # => 8
それを使って自分で構築しましょうpartial
:
(def inc (partial add 1))
ここでは、の最初の引数に1が読み込まれた別の関数を返しますadd
。As add
は2つの引数を取るので、新しいinc
関数はb
引数のみを必要とします。1つはすでに部分的に適用されているため、以前のように2つの引数は必要ありません。したがってpartial
、デフォルト値が事前に提供されている新しい関数を作成するためのツールです。そのため、関数型言語では、関数は一般的な引数から特定の引数へと引数を並べ替えます。これにより、他の関数を構築するためにそのような関数を再利用しやすくなります。
次に、その言語が内省的に理解できるほど賢く、add
2つの引数が必要かどうか想像してみてください。バランスをとるのではなく、1つの引数を渡したときに、関数が引数を部分的に適用した場合は、後からもう1つの引数を提供するつもりであると理解して、引数を渡したとしたらどうでしょう。その後、inc
明示的にを使用せずに定義できますpartial
。
(def inc (add 1)) #partial is implied
これは、一部の言語の動作方法です。関数をより大きな変換に構成したい場合に非常に役立ちます。これはトランスデューサーにつながります。
私はこの記事とそれが参照している記事がカレーをよりよく理解するのに役立つとわかりました:http : //blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx
他の人が述べたように、それは1つのパラメータ関数を持つための単なる方法です。
これは、渡されるパラメーターの数を想定する必要がないので便利です。そのため、2つのパラメーター、3つのパラメーター、および4つのパラメーター関数は必要ありません。
他のすべての回答と同じように、カリー化は部分的に適用される関数の作成に役立ちます。JavaScriptは、自動カレーのネイティブサポートを提供していません。したがって、上記の例は実際のコーディングには役立たない場合があります。livescriptにはいくつかの優れた例があります(これは基本的にjsにコンパイルされます) http://livescript.net/
times = (x, y) --> x * y
times 2, 3 #=> 6 (normal use works as expected)
double = times 2
double 5 #=> 10
上記の例では、引数の数が少ない場合、livescriptは新しいカレー関数を生成します(double)
Curryはコードを簡略化できます。これは、これを使用する主な理由の1つです。カリー化とは、n個の引数を受け入れる関数を、1つの引数のみを受け入れるn個の関数に変換するプロセスです。
原則は、クロージャー(クロージャー)プロパティを使用して、渡された関数の引数を渡し、それらを別の関数に格納して戻り値として扱い、これらの関数がチェーンを形成し、最後の引数が渡されて完了します。操作。
これの利点は、一度に1つのパラメーターを処理することによってパラメーターの処理を単純化できることです。これにより、プログラムの柔軟性と可読性も向上します。これにより、プログラムが管理しやすくなります。また、コードを小さな部分に分割すると、再利用しやすくなります。
例えば:
function curryMinus(x)
{
return function(y)
{
return x - y;
}
}
var minus5 = curryMinus(1);
minus5(3);
minus5(5);
私もできます...
var minus7 = curryMinus(7);
minus7(3);
minus7(5);
これは、複雑なコードをきちんと作成し、非同期のメソッドを処理する場合などに非常に便利です。
カリー化された関数は、1つではなく複数の引数リストに適用されます。
これは、カレーではない通常の関数で、2つのIntパラメーターxとyを追加します。
scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int,y: Int)Int
scala> plainOldSum(1, 2)
res4: Int = 3
これはカレー化された同様の関数です。2つのIntパラメータの1つのリストの代わりに、この関数をそれぞれ1つのIntパラメータの2つのリストに適用します。
scala> def curriedSum(x: Int)(y: Int) = x + y
curriedSum: (x: Int)(y: Int)Intscala> second(2)
res6: Int = 3
scala> curriedSum(1)(2)
res5: Int = 3
ここで起こっていることは、を呼び出すとcurriedSum
、実際には2つの従来の関数呼び出しが連続して行われるということです。最初の関数呼び出しは、という名前の単一のIntパラメータを取りx
、2番目の関数の関数値を返します。この2番目の関数はIntパラメータを取ります
y
。
以下first
は、の最初の従来の関数呼び出しが何をするかを精神的に実行するという名前の関数curriedSum
です。
scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int
最初の関数に1を適用すると、つまり最初の関数を呼び出して1を渡すと、2番目の関数が生成されます。
scala> val second = first(1)
second: (Int) => Int = <function1>
2番目の関数に2を適用すると、結果が得られます。
scala> second(2)
res6: Int = 3
カリー化の例は、現時点でパラメーターの1つしか知らない関数がある場合です。
例えば:
func aFunction(str: String) {
let callback = callback(str) // signature now is `NSData -> ()`
performAsyncRequest(callback)
}
func callback(str: String, data: NSData) {
// Callback code
}
func performAsyncRequest(callback: NSData -> ()) {
// Async code that will call callback with NSData as parameter
}
ここでは、コールバックの2番目のパラメーターがわからないので、それをperformAsyncRequest(_:)
関数に送信するために別のラムダ/クロージャーを作成する必要があります。
func callback
自体を返しますか?これは@と呼ばれているcallback(str)
ためlet callback = callback(str)
、コールバックは単なる戻り値func callback
func callback(_:data:)
2つのパラメーターを受け入れます。ここではString
、1つのみを指定しているため、次のパラメーター(NSData
)をlet callback
待機しています。これが、データが渡されるのを待機している別の関数である理由です
以下は、n noを使用した関数カリー化のジェネリックおよび最短バージョンの例です。パラメータの。
const add = a => b => b ? add(a + b) : a;
const add = a => b => b ? add(a + b) : a;
console.log(add(1)(2)(3)(4)());
ここでは、C#でのカリー実装の簡単な説明を見つけることができます。コメントで、カレーがいかに役立つかを示しました。
public static class FuncExtensions {
public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
{
return x1 => x2 => func(x1, x2);
}
}
//Usage
var add = new Func<int, int, int>((x, y) => x + y).Curry();
var func = add(1);
//Obtaining the next parameter here, calling later the func with next parameter.
//Or you can prepare some base calculations at the previous step and then
//use the result of those calculations when calling the func multiple times
//with different input parameters.
int result = func(1);
カリー化は、Javaスクリプトの高次関数の1つです。
カリーは多くの引数の関数であり、最初の引数を取り、残りの引数を使用して値を返す関数を返すように書き換えられます。
混乱していますか?
例を見てみましょう、
function add(a,b)
{
return a+b;
}
add(5,6);
これは次のカレー機能に似ています、
function add(a)
{
return function(b){
return a+b;
}
}
var curryAdd = add(5);
curryAdd(6);
では、このコードは何を意味するのでしょうか?
もう一度定義を読んで、
カリー化は多くの引数の関数であり、最初の引数を取り、残りの引数を使用して値を返す関数を返すように書き換えられます。
それでも、混乱していますか?深く説明させてください!
この関数を呼び出すと、
var curryAdd = add(5);
それはあなたにこのような関数を返します、
curryAdd=function(y){return 5+y;}
したがって、これは高次関数と呼ばれます。つまり、ある関数を順番に呼び出すと別の関数が返され、これは高次関数の正確な定義です。これが、伝説のJavaスクリプトの最大の利点です。カレーに戻って、
この行は、2番目の引数をcurryAdd関数に渡します。
curryAdd(6);
その結果、
curryAdd=function(6){return 5+6;}
// Which results in 11
ここでカレーの使い方を理解してください。だから、利点に来て、
なぜカレーなのか?
コードの再利用性を利用しています。少ないコード、少ないエラー。あなたはそれがより少ないコードであるかどうか尋ねるかもしれませんか?
ECMAスクリプト6の新機能の矢印関数でそれを証明できます。
はい!ECMA 6は、矢印関数と呼ばれる素晴らしい機能を提供します。
function add(a)
{
return function(b){
return a+b;
}
}
アロー関数の助けを借りて、上記の関数を次のように書くことができます、
x=>y=>x+y
かっこいい?
したがって、コードを減らし、バグを減らすことができます!!
これらの高次関数の助けを借りて、バグのないコードを簡単に開発できます。
挑戦します!
カレーとは何か理解できたでしょうか。説明が必要な場合は、こちらからコメントしてください。
ありがとう、良い一日を!
「ReasonMLでのカレー」の例があります。
let run = () => {
Js.log("Curryed function: ");
let sum = (x, y) => x + y;
Printf.printf("sum(2, 3) : %d\n", sum(2, 3));
let per2 = sum(2);
Printf.printf("per2(3) : %d\n", per2(3));
};
curry
およびuncurry
関数です。ここで重要なことは、これらの同型があらかじめ固定されているため、言語に「組み込まれている」ということです。