カリー化とは?
C ++でカリー化を行うにはどうすればよいですか?
STL コンテナのバインダーについて説明してください。
カリー化とは?
C ++でカリー化を行うにはどうすればよいですか?
STL コンテナのバインダーについて説明してください。
要するに、カリー化は関数f(x, y)
を取り、固定された を指定するとY
、新しい関数g(x)
が得られます。
g(x) == f(x, Y)
この新しい関数は、引数が 1 つしか指定されていない状況で呼び出すことができ、その呼び出しをf
固定Y
引数で元の関数に渡します。
STL のバインダーを使用すると、C++ 関数に対してこれを行うことができます。例えば:
#include <functional>
#include <iostream>
#include <vector>
using namespace std;
// declare a binary function object
class adder: public binary_function<int, int, int> {
public:
int operator()(int x, int y) const
{
return x + y;
}
};
int main()
{
// initialise some sample data
vector<int> a, b;
a.push_back(1);
a.push_back(2);
a.push_back(3);
// here we declare a function object f and try it out
adder f;
cout << "f(2, 3) = " << f(2, 3) << endl;
// transform() expects a function with one argument, so we use
// bind2nd to make a new function based on f, that takes one
// argument and adds 5 to it
transform(a.begin(), a.end(), back_inserter(b), bind2nd(f, 5));
// output b to see what we got
cout << "b = [" << endl;
for (vector<int>::iterator i = b.begin(); i != b.end(); ++i) {
cout << " " << *i << endl;
}
cout << "]" << endl;
return 0;
}
tr1 を使用して、Gregg の例を単純化します。
#include <functional>
using namespace std;
using namespace std::tr1;
using namespace std::tr1::placeholders;
int f(int, int);
..
int main(){
function<int(int)> g = bind(f, _1, 5); // g(x) == f(x, 5)
function<int(int)> h = bind(f, 2, _1); // h(x) == f(2, x)
function<int(int,int)> j = bind(g, _2); // j(x,y) == g(y)
}
Tr1 機能コンポーネントを使用すると、C++ でリッチな機能スタイル コードを記述できます。同様に、C++0x では、インライン ラムダ関数でこれを行うこともできます。
int f(int, int);
..
int main(){
auto g = [](int x){ return f(x,5); }; // g(x) == f(x, 5)
auto h = [](int x){ return f(2,x); }; // h(x) == f(2, x)
auto j = [](int x, int y){ return g(y); }; // j(x,y) == g(y)
}
C++ は、一部の関数型プログラミング言語が実行する豊富な副作用分析を提供しませんが、const 分析と C++0x ラムダ構文が役立ちます。
struct foo{
int x;
int operator()(int y) const {
x = 42; // error! const function can't modify members
}
};
..
int main(){
int x;
auto f = [](int y){ x = 42; }; // error! lambdas don't capture by default.
}
それが役立つことを願っています。
Greg が示したプロセスをより用途の広いものにするBoost.Bindを見てください。
transform(a.begin(), a.end(), back_inserter(b), bind(f, _1, 5));
これはの 2 番目の引数にバインド5
されます。f
これはカリー化ではないことに注意してください(代わりに、部分的な適用です)。ただし、一般的な方法でカリー化を使用することは C++ では難しく (実際、それが可能になったのはごく最近のことです)、代わりに部分適用がよく使用されます。
他の回答はバインダーをうまく説明しているので、ここではその部分を繰り返しません。ここでは、C++0x でラムダを使用してカリー化と部分適用を実行する方法のみを示します。
コード例: (コメントで説明)
#include <iostream>
#include <functional>
using namespace std;
const function<int(int, int)> & simple_add =
[](int a, int b) -> int {
return a + b;
};
const function<function<int(int)>(int)> & curried_add =
[](int a) -> function<int(int)> {
return [a](int b) -> int {
return a + b;
};
};
int main() {
// Demonstrating simple_add
cout << simple_add(4, 5) << endl; // prints 9
// Demonstrating curried_add
cout << curried_add(4)(5) << endl; // prints 9
// Create a partially applied function from curried_add
const auto & add_4 = curried_add(4);
cout << add_4(5) << endl; // prints 9
}
C++14 を使用している場合は、非常に簡単です。
template<typename Function, typename... Arguments>
auto curry(Function function, Arguments... args) {
return [=](auto... rest) {
return function(args..., rest...);
}; // don't forget semicolumn
}
その後、次のように使用できます。
auto add = [](auto x, auto y) { return x + y; }
// curry 4 into add
auto add4 = curry(add, 4);
add4(6); // 10
カリー化は、複数の引数を取る関数を、それぞれ 1 つの引数を持つネストされた関数のシーケンスに縮小する方法です。
full = (lambda a, b, c: (a + b + c))
print full (1, 2, 3) # print 6
# Curried style
curried = (lambda a: (lambda b: (lambda c: (a + b + c))))
print curried (1)(2)(3) # print 6
カリー化は、事前定義された値を持つ他の関数の単なるラッパーである関数を定義し、単純化された関数を渡すことができるため便利です。C++ STL バインダーは、これを C++ で実装します。
これらのリンクは関連しています:
ウィキペディアのラムダ計算ページには、カリー化の明確な例があります
http://en.wikipedia.org/wiki/Lambda_calculus#Motivation
この論文では、C/C++ でのカリー化について扱います
http://asg.unige.ch/site/papers/Dami91a.pdf