「カレー」とは何ですか?
-
09-06-2019 - |
質問
いくつかの記事やブログでカリー関数への言及を見てきましたが、適切な説明 (少なくとも意味のある説明が見つかりません!) が見つかりません。
解決
カリー化とは、複数の引数を取る関数を、引数の一部を取る一連の関数に分解することです。JavaScript の例を次に示します。
function add (a, b) {
return a + b;
}
add(3, 4); // returns 7
これは、2 つの引数 a と b を受け取り、それらの合計を返す関数です。次に、この関数をカリー化します。
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 を生成します。
他のヒント
関数の代数では、複数の引数 (または N タプルである同等の 1 つの引数) を取る関数を扱うのはやや洗練されていません。しかし、Moses Schönfinkel (そして独立して Haskell Curry) が証明したように、その必要はありません。必要なのは、引数を 1 つ取る関数だけです。
では、自然に次のように表現してしまうものにどう対処すればよいでしょうか。 f(x,y)
?まあ、あなたはそれを次のものと同等だと考えています f(x)(y)
-- f(x)
, 、 あれを呼べ g
, は関数であり、その関数を次の関数に適用します。 y
. 。言い換えれば、引数を 1 つ取る関数しかありませんが、それらの関数の一部は他の関数を返します (これらの関数も引数を 1 つ取ります;-)。
いつものように、 ウィキペディア には、これに関する優れた要約エントリがあり、多くの有用な指針 (おそらくあなたのお気に入りの言語に関する指針も含まれます;-) と、もう少し厳密な数学的処理が含まれています。
具体的な例を次に示します。
物体に作用する重力を計算する関数があるとします。公式が分からなくても調べればわかります ここ. 。この関数は、必要な 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 -> f x」は、C# の x => f(x) と同等のラムダ関数です。この関数は、カリー化したい関数を入力し、次の関数を返します。 単一の引数を取ります そして、最初の引数が入力引数に設定された指定された関数を返します。
前の例を使用すると、次のように f のカレーを取得できます。
let curryf = curry f
その後、次のことが可能になります:-
let f1 = curryf 1
これにより、f1 y z = 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...
カリー化された関数は、最初の引数を受け入れ、2 番目の引数を受け入れる関数を返すように書き換えられた複数の引数の関数です。これにより、複数の引数を持つ関数に、初期引数の一部を部分的に適用することができます。
以下は 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 がこれを実装する方法における関数の区別。
もし分かれば partial
あなたは半分のところにいる。という考え partial
関数に引数を事前に適用し、残りの引数のみを必要とする新しい関数を返すことです。この新しい関数が呼び出されるとき、プリロードされた引数と、それに指定された引数が含まれます。
Clojure で +
は関数ですが、物事を明確にするためです。
(defn add [a b] (+ a b))
ご存知かもしれませんが、 inc
この関数は、渡された数値に 1 を加算するだけです。
(inc 7) # => 8
を使用して自分で構築しましょう partial
:
(def inc (partial add 1))
ここでは、最初の引数に 1 がロードされた別の関数を返します。 add
. 。として add
new は 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 パラメーターの関数が必要ないという点で便利です。
カリーを使用するとコードを簡素化できます。これがこれを使用する主な理由の 1 つです。カリー化は、n 個の引数を受け入れる関数を、1 つの引数のみを受け入れる n 個の関数に変換するプロセスです。
原理としては、渡された関数の引数をクロージャ(closure)プロパティを使って渡し、別の関数に格納して戻り値として扱い、これらの関数がチェーンを形成し、最後の引数を渡して完了するというものです。操作。
この利点は、一度に 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);
これは、複雑なコードをすっきりさせたり、非同期メソッドなどを処理したりするのに非常に役立ちます。
カリー化とは、関数を呼び出し可能なものから次のように変換することです。 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 を指定すると、呼び出しが元の複数引数の合計に渡されます。
カレー関数は、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(_:)
別のラムダ/クロージャを作成して関数に送信する必要があります。
他のすべての回答と同様に、カリー化は部分的に適用された関数を作成するのに役立ちます。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)
ここでは、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);