C++のラムダ記法ってありますよね。
あれって何が便利なのですか??
暗黙のうちにクラスが生成されているんですよね。
よく関数型プログラミングとかを調べると、javascriptのラムダ式・・・とかが出てきます。
C++のラムダ式も関数型プログラミングと関係があるんですか?
関数型プログラミングはオブジェクト指向の正当な後継である
今さら聞けない!関数型プログラミングとは【初心者向け】
関数型プログラミングのメリットがわかれば、ラムダ式のメリットも分かるのではないかと思っているのですが・・・
この関数型プログラミングとは一体なんなのか・・・調べてみたのですが、なんかピンと来ません。
[自分がラムダ式を使っていて思ったこと]
・関数を記述するのにイチイチmain関数の外にカーソルを移動しなくて済む。
・ラムダ式の記述の仕方がかっこいい♡
(関数の中に関数の定義を記述できるのはgcc拡張のトランポリンコードと同じですよね。
トランポリンが便利だと思ったことはないんですが・・・)
と・・・これくらいですかね・・・
私の勝手な想像(妄想) ↓
//数学的な書き方 10 sum = ∑ k // 55 k = 1
main関数内・・・
C++
1//Normal 2 3int sum = 0; 4 5for(int i = 1; i < 11; i ++ ) 6 sum += i;
C++
1//Lambda 2 3auto f = []{ 4 int sum = 0; 5 for(int i = 1; i < 11; i++) 6 sum += i; 7 return sum; 8}(); 9 10// intは別にautoでも良い
このように見比べるとですね・・・
キリの良い単位が関数単位で分割されている・・・といいますか・・・なんといいますか。
関数単位で処理を扱っているように見えませんか??
処理が細かくわけられている・・・みたいな?
だから何??って感じなんですが・・・
ラムダ記法による関数って何が便利なのでしょうか??
教えてください。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答5件
0
ベストアンサー
ラムダ式が真価を発揮するのはクロージャを使っているときです。C++ではキャプチャという機能で限定的なクロージャを提供しています。(なぜ限定的かは後述)
例えば、下記のようなコードを見てください。
C++
1#include <functional> 2#include <iostream> 3 4std::function<int(int)> createMultiple(int mp) 5{ 6 return [mp](int i) { return i * mp; }; 7} 8 9int main() 10{ 11 auto triple = createMultiple(3); 12 auto quadruple = createMultiple(4); 13 int x = 42; 14 std::cout << x << u8"の三倍は" << triple(x) << u8"で、四倍は" 15 << quadruple(x) << u8"です。" << std::endl; 16 return 0; 17}
上は任意の数の倍数にする関数を作る関数です。関数を返す関数自体はラムダ式を使わなくても可能ですが、重要なのは、与えられた引数を用いて関数を作るという所です。createMultiple()
に与えた引数をキャプチャでコピーしておくことによって、その引数を使った(内包した)関数が作れています。通常の関数定義ではキャプチャの機能がありませんので、同じことはできません。
※ createMultiple()
が終了後にmp
は破棄されるため、ここでのキャプチャはコピーでなくてはなりません。参照にした場合の動作は未定義になります。
もし、ラムダ式を使わなかったら、下記のようにクラスを作る必要があります。
C++
1#include <iostream> 2 3class Multiple 4{ 5private: 6 int mp; 7 8public: 9 Multiple(int mp) : mp(mp) {} 10 int operator()(int i) { return i * this->mp; } 11}; 12 13int main() 14{ 15 Multiple triple(3); 16 Multiple quadruple(4); 17 int x = 42; 18 std::cout << x << u8"の三倍は" << triple(x) << u8"で、四倍は" 19 << quadruple(x) << u8"です。" << std::endl; 20 return 0; 21}
やっていることはほぼ一緒ではあるのですが、ラムダ式よりも冗長で複雑です。
上のサンプルコードについて、テンプレートを使えば似たようなことは出来るという人がいるかも知れません。
C++
1#include <functional> 2#include <iostream> 3 4template <int mp> int multiple(int i) { return i * mp; } 5 6int main() 7{ 8 auto triple = multiple<3>; 9 auto quadruple = multiple<4>; 10 int x = 42; 11 std::cout << x << u8"の三倍は" << triple(x) << u8"で、四倍は" 12 << quadruple(x) << u8"です。" << std::endl; 13 return 0; 14}
残念ながら、これは3
や4
が静的に決定できるから出来るだけで、もし、プログラムの引数で指定するなどと言った場合にテンプレートの方法では実現できません。
【キャプチャは限定的なクロージャ】
本来、クロージャはその関数が定義されるときの環境をその関数に内包させることですが、C++では関数が終わると同時に環境は捨てられます。そのため、キャプチャという機能で、その環境でアクセス可能な変数をコピーまたは参照するようになっています。環境そのものが関数に内包される他の言語とは性質が異なりますので注意が必要です。なお、環境を内包するにはGCが存在しなければ制御することは難しいと考えらるため、GCが無いC++において、キャプチャは現実的な選択だと思われます。
上記以外に関数を引数に取る関数(std::sort()
等)に対して、その場で引数にする関数を書けるという利点がありますが、ちょっと便利になる程度のおまけ程度の機能だと私は思っています。
【追記:ラムダ式と関数型プログラミングとの関係】
関数型プログラミングとはプログラミングにおける考え方(コードの組み立て方)の一つです。ただの考え方なので、言語仕様に強く依存するわけではありません。最低限、関数が第一級オブジェクトにできる(または擬似的にできる)のであれば、どのような言語でもほとんど取り入れることが出来ると考えられています。しかし、関数型プログラミングがしやすいかしにくいかは別の話です。
ラムダ式は関数型プログラミングにおいて必須であるというわけでは無く、無いと不便で仕方が無いという程度のものです。関数型プログラミングはさまざまな手法の寄り集まりではありますが、クロージャーを備えたラムダ式を使うことを前提としている手法も多くあります(上のラムダ式のコードはその一例です)。擬似的なラムダ式で実現することも不可能ではないですが、大変面倒で冗長な処理になってしまうことでしょう。つまり、C++で関数型プログラミングを行う場合、ラムダ式は、必須とは言えないまでも、無い場合は酷く冗長でわかりにくいコードになってしまうと言うことです。
では、関数型プログラミングとは何か?どうしてもそう言えるのか?を説明しようとすると、ちょっと感覚的なこともあるので、関数型プログラミングそのものを知らないと言葉で説明するのは難しいです。Haskellのような本物の関数型言語を一度やってみるといいかもしれません。
ついでに、ラムダ式がなかったC++11より前のC++03時代はどうだったのかというと、boostを使って頑張っている記事を見つけたので、紹介しておきます。
関数型言語 C++ - 兼雑記
C++ で SICP - memologue
※ 2006年の記事ですので、C++11以降では全く事情が異なることに注意してください。
投稿2018/01/28 13:09
編集2018/01/28 14:24総合スコア21737
0
ご質問のソースのように単品でラムダ式を利用することには、あまりメリットはないと思います。
ラムダ式の利用シーンは、代表的なものとしては<algorithm>
の関数に多いですが、関数オブジェクトが要求される関数呼び出しですね。
例えば、配列を降順にソートする場合、ラムダ式導入前はこんな感じに書いていました(コード例なのでstd::greaterを使えという突っ込みは無しで)。
C++
1std::vector<int> vect{4, 2, 3, 1, 5}; 2struct Comp 3{ 4 bool operator ()(int left, int right) const 5 { 6 return left > right; 7 } 8}; 9std::sort(vect.begin(), vect.end(), Comp());
ラムダ式を使うとこう書けます。
C++
1std::vector<int> vect{4, 2, 3, 1, 5}; 2std::sort(vect.begin(), vect.end(), [](auto left, auto right){return left > right;});
条件を関数呼び出しにインラインで書けるようになったので可読性も上がります。
他の使い道としては、条件によって処理の一部分だけ変えたいというときに使うとすっきり書けることがあります。
C++
1int sumx(const std::vector<int> &vect, bool odd) 2{ 3 std::function<bool(int)> cond; 4 if(odd) 5 cond = [](int x){return x % 2 != 0;}; 6 else 7 cond = [](int x){return x % 2 == 0;}; 8 9 int sum = 0; 10 for(auto&& x : vect) 11 { 12 if(cond(x)) 13 sum += x; 14 } 15 return sum; 16}
投稿2018/01/28 11:36
編集2018/01/28 12:06総合スコア5944
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/01/28 12:23
2018/01/28 12:32
2018/01/28 12:56
2018/01/28 12:59
2018/01/28 13:02
0
こんにちは。
1箇所からしか呼ばないことが確実だけど、関数にしないといけないような時に良く使います。
そのメリットは、「関数を記述するのにイチイチmain関数の外にカーソルを移動しなくて済む。」ですね。
他に両刃の剣的な部分もありますが、キャプチャがありがたいです。
ローカル変数をキャプチャしてくれるので一々パラメータで渡さなくても渡ります。
キャプチャは参照とコピーの2種類があります。コピーは当然重いですが安全です。参照は軽いですが中々危険です。
ラムダ式を定義した関数がreturnしてローカル変数が寿命を迎えた後でも、ラムダ式が生きているケースがあります。(例えば、ラムダ式を実行するスレッドを起動した関数がreturnした時など)
その時、その関数のローカル変数を参照キャプチャしていると、そのキャプチャした変数は無効なスタックを指しているのでなかなか痛いです。
ローカル変数を参照でreturnしたような状態になります。こちらは警告してくれる事が多いので比較的安全ですが、参照キャプチャは警告でないようです。
投稿2018/01/28 10:38
総合スコア23272
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/01/28 10:59
2018/01/28 11:29 編集
2018/01/28 11:47
2018/01/28 11:48
2018/01/28 11:50
2018/01/28 12:22 編集
2018/01/28 12:24
2018/01/28 12:31
2018/01/28 12:36 編集
2018/01/28 12:45
2018/01/28 12:52
2018/01/28 12:53
0
あくまでも便利なもの程度の認識です。単純に使い手の好み、と言ってしまえばそれまでです。内部関数なり、基本構文なりで代用できるならば必要とまではいえません。
とはいえ、プログラムを簡素に記述でき、外部と名前重複の懸念がないというのは魅力的ではあります。今やプログラムの規模は膨大になってきているので、ソースを見やすくすることも技術の一つであるといえます。
以下、参考
さらにいうならば、例えば総合開発環境の高機能化への追従も社会通念上、事実上の必要事項であるわけで、一種の派閥や政治論にも波及するところです。使い手の好みの問題、といっても、複数人で作業する場合などでは決して簡単には割り切ることができないものだったりします。偉い人の好みというだけで、やり方が決定するということも多々ありえます。
以下、追記
関数型言語のお話もされているようなので、これについても述べます。関数型言語といえば、私はHaskellが代表として筆頭だと思っております。最大の理由は「変数を使用できないこと(初期値を変更できない)」です。(この制約のため、多くの処理を再帰によって記述することになります)
この制約のおかげでHaskellは単独で可能な処理が著しく制限される一方で、他の言語にないメリットがあります。「外界に影響を与えずにデータを変換する」ということに着目すれば下請けとして非常に有用であり、ラムダに通じるものであると考えております。
投稿2018/01/28 11:15
編集2018/01/28 13:16総合スコア4830
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/01/28 13:21
0
なおC++で関数型プログラミングをやろうとして時期尚早だったと諦めている検証例としては
さいきょうの関数型言語はC++だったのかもしれないという可能性は慎重に検討されねばならない - Qiita
があります。
投稿2018/01/29 09:19
総合スコア5852
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/01/28 13:23
2018/01/28 13:31
2018/01/28 13:43 編集
2018/01/28 13:40
2018/01/28 13:46
2018/01/28 13:54
2018/01/28 14:29
2018/01/28 14:30 編集