それでは、始めましょう(そして楽しみが一緒に来るかもしれません)。 このテキストはSlideshareのスライドとしても利用できます。 この記事の著者は、JSONの作成者Douglas Crockfordに触発されました。
引数を取り、同じ引数を返すIdentity関数:
auto Identity = [](auto x) { return x; }; Identity(3); // 3
翻訳者注 :C ++ 11と比較して新しいのは、型名を指定しない機能です。
関数add、sub、およびmulは、2つの引数を取り、それぞれ合計、差、積を返します。
auto add = [](auto x, auto y) { return x + y; }; auto sub = [](auto x, auto y) { return x - y; }; auto mul = [](auto x, auto y) { return x * y; };
引数を取り内部クラスのインスタンスを返すidentityf関数を呼び出すと、元の引数が返されます。
auto identityf = [](auto x) { class Inner { int x; public: Inner(int i): x(i) {} int operator() () { return x; } }; return Inner(x); }; identityf(5)(); // 5
オブジェクトではなく関数を返す別のidentityf実装(はい、関数を返すことができるようになりました):
auto identityf = [](auto x) { return [=]() { return x; }; }; identityf(5)(); // 5
注:ラムダ関数≠クロージャ:
- Lambdaは単なる匿名関数です。
- クロージャーは、宣言された環境のオブジェクトを使用する関数です。 前の例の2行目では、等号は「コンテキストのキャプチャ」を示しています。
- すべてのラムダがクロージャではなく、すべてのクロージャがラムダではありません。
- C ++のクロージャは、呼び出すことができる一般的なオブジェクトです。
- クロージャは、使用するオブジェクトの寿命を延ばしません(このため、shared_ptrを使用します)。
指定された間隔から数値を返すジェネレーター関数を返す関数:
auto fromto = [](auto start, auto finish) { return [=]() mutable { if(start < finish) return start++; else throw std::runtime_error("Complete"); }; }; auto range = fromto(0, 10); range(); // 0 range(); // 1
一度に1つずつ番号を取り、それらを追加する関数:
auto addf = [](auto x) { return [=](auto y) { return x+y; }; }; addf(5)(4); // 9
別の関数の引数を交換する関数:
auto swap =[](auto binary) { return [=](auto x, auto y) { return binary(y, x); }; }; swap(sub)(3, 2); // -1
バイナリ関数を受け取り、バイナリに引数を2回渡す単項関数を返す2回関数:
auto twice =[](auto binary) { return [=](auto x) { return binary(x, x); }; }; twice(add)(11); // 22
バイナリ関数を受け取り、2つの引数を順番に受け取る関数を返す関数:
auto applyf = [](auto binary) { return [=](auto x) { return [=](auto y) { return binary(x, y); }; }; }; applyf(mul)(3)(4); // 12
バイナリ関数と引数を取り、2番目の引数を取る関数を返すカレー関数:
auto curry = [](auto binary, auto x) { return [=](auto y) { return binary(x, y); }; }; curry(mul, 3)(4); // 12
注: カリー化 (schrying、schönfinkeling)は、いくつかの引数を取る関数を、1つの引数を取る関数のチェーンに変換することです。
- λ分析では、すべての関数は1つの引数のみを取ります。
- Haskellを学習するには、カリー化の仕組みを理解する必要があります。
- カリー化は、関数の部分的な適用です。
関数の部分的な使用:
auto addFour = [](auto a, auto b, auto c, auto d) { return a+b+c+d; }; auto partial = [](auto func, auto a, auto b) { return [=](auto c, auto d) { return func(a, b, c, d); }; }; partial(addFour,1,2)(3,4); //10
3つのオプション、新しい関数を作成せずに引数に1を追加する関数を取得する方法:
auto inc = curry(add, 1); auto inc = addf(1); auto inc = applyf(add)(1);
機能の構成の実装:
auto composeu =[](auto f1, auto f2) { return [=](auto x) { return f2(f1(x)); }; }; composeu(inc1, curry(mul, 5))(3) // (3 + 1) * 5 = 20
バイナリ関数を受け取り、一度しか呼び出せないように変更する関数:
auto once = [](auto binary) { bool done = false; return [=](auto x, auto y) mutable { if(!done) { done = true; return binary(x, y); } else throw std::runtime_error("once!"); }; }; once(add)(3,4); // 7
バイナリ関数を受け取り、2つの引数とコールバックを受け取る関数を返す関数:
auto binaryc = [](auto binary) { return [=](auto x, auto y, auto callbk) { return callbk(binary(x,y)); }; }; binaryc(mul)(5, 6, inc) // 31 binaryc(mul)(5, 6, [](int a) { return a+1; }); //
最後に、次の3つの関数を作成します。
- 単位はidentityfと同じです。
- stringify-引数を文字列に変換し、ユニットを適用します。
- bind-ユニットの結果を受け取り、コールバックを受け取り、それをユニットの結果に適用した結果を返す関数を返します。
auto unit = [](auto x) { return [=]() { return x; }; }; auto stringify = [](auto x) { std::stringstream ss; ss << x; return unit(ss.str()); }; auto bind = [](auto u) { return [=](auto callback) { return callback(u()); }; };
次に、すべてが機能することを確認します。
std::cout << "Left Identity " << stringify(15)() << "==" << bind(unit(15))(stringify)() << std::endl; std::cout << "Right Identity " << stringify(5)() << "==" << bind(stringify(5))(unit)() << std::endl;
ユニットとバインド関数について何がそんなに面白いですか? 実際、これはモナドです。
著者のブログシリーズの2番目の投稿を読んでください。