質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
関数型プログラミング

関数型プログラミングとは、関数を用いて演算子を構築し、算出し、コンピュータプログラムを構成する枠組みです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

5回答

12502閲覧

ラムダ式って何が便利なのです??

strike1217

総合スコア651

関数型プログラミング

関数型プログラミングとは、関数を用いて演算子を構築し、算出し、コンピュータプログラムを構成する枠組みです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

3グッド

6クリップ

投稿2018/01/28 10:10

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でも良い

このように見比べるとですね・・・
キリの良い単位が関数単位で分割されている・・・といいますか・・・なんといいますか。
関数単位で処理を扱っているように見えませんか??
処理が細かくわけられている・・・みたいな?

だから何??って感じなんですが・・・

ラムダ記法による関数って何が便利なのでしょうか??
教えてください。

Mooktoo, BeatStar, umyu👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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}

残念ながら、これは34が静的に決定できるから出来るだけで、もし、プログラムの引数で指定するなどと言った場合にテンプレートの方法では実現できません。


【キャプチャは限定的なクロージャ】

本来、クロージャはその関数が定義されるときの環境をその関数に内包させることですが、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
raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/01/28 13:23

「ラムダ式よりも冗長で複雑です。 」 つまり、簡略にコードを記述できるのがメリットである。 ということですよね? 「与えられた引数を用いて関数を作る」 なるほど!!
catsforepaw

2018/01/28 13:31

> ちょっと便利になる程度のおまけ程度の機能だと私は思っています。 その「ちょっと」の積み重ねが大きいのですよね。STLを利用する際はそれが必要なケースが非常に多いので。
raccy

2018/01/28 13:43 編集

> 簡略にコードを記述できるのがメリット 短く書けるというのはそれほど重要では無く、理解しやすいかの方が重要です。 ラムダ式とクラスの例はやっていること(実現できていること)はほぼ一緒なのに、考え方が全く異なります。ラムダ式バージョンは関数型プログラミングでよく見られる考え方なのですが、関数型プログラミングを知らない人にとってはクラスの方がわかりやすいというかもしれません。逆に、関数型プログラミングに慣れた人や関数型プログラミングの作法を取り入れてコーディングする人にとってはラムダ式の方が理解しやすいと考えるでしょう。ただ、C++で純粋な関数型プログラミングをそのまま採用するにはかなり無理が出てくる(特にパフォーマンスの面で)ので、昔ながらのC++erは関数型プログラミングのように書けることはそれほど重要視していないと思われます。
raccy

2018/01/28 13:40

> その「ちょっと」の積み重ねが大きい そうですね。おまけでもいいですし、キャプチャなんて無くても良いから、ラムダ式がCにも欲しいと思ったことが何度あったことか…。
strike1217

2018/01/28 13:46

> 理解しやすいかの方が重要です。 そうなんですか! > ラムダ式バージョンは関数型プログラミングでよく見られる考え方 左様ですか! やはり関連性があるんですね!
strike1217

2018/01/28 13:54

与えられた引数を用いて関数を作る というのが関数型プログラミングでよく見られる手法ということですよね。 ふむーー。
strike1217

2018/01/28 14:29

わかりました。 関数型プログラミングについては自分でもっと調べてみます。 ラムダへの理解も深まるかもしれません。
raccy

2018/01/28 14:30 編集

関数型プログラミングとの関係について追記しました。 なお、ラムダ式を使えば関数型プログラミングになるわけでも、関数型プログラミングでなければラムダ式を使ってはいけないわけでもありません。関数型プログラミングとは全く関係無いところでラムダ式を使っても別に問題はありません。
guest

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
catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/01/28 11:47

sortでの例示はよく見かけますね。
strike1217

2018/01/28 12:23

「条件によって処理の一部分だけ変えたい」 なるほど!!
strike1217

2018/01/28 12:32

無名関数ということが一番のメリットなんですかね〜〜
catsforepaw

2018/01/28 12:56

無名関数がメリットというのはちょっと変ですね。C++における無名関数の記述方法がラムダ式というだけなので。 メリットとしては以下の2点にあると思います。 - 関数の中身を式やロジックの中にインラインで書ける タイプ量の削減や可読性の向上が期待できる。 - 関数をオブジェクトとして扱う 変数や引数で受け渡し可能。
strike1217

2018/01/28 12:59

> 関数の中身を式やロジックの中にインラインで書ける ああ。 やはりこれが便利な点なのですね!
strike1217

2018/01/28 13:02

てっきりもっと奥が深いものなのかと勝手に想像していました。 - 関数の中身を式やロジックの中にインラインで書ける - 関数をオブジェクトとして扱う この2点なのですね! わかりました。
guest

0

こんにちは。

1箇所からしか呼ばないことが確実だけど、関数にしないといけないような時に良く使います。
そのメリットは、「関数を記述するのにイチイチmain関数の外にカーソルを移動しなくて済む。」ですね。

他に両刃の剣的な部分もありますが、キャプチャがありがたいです。
ローカル変数をキャプチャしてくれるので一々パラメータで渡さなくても渡ります。


キャプチャは参照とコピーの2種類があります。コピーは当然重いですが安全です。参照は軽いですが中々危険です。
ラムダ式を定義した関数がreturnしてローカル変数が寿命を迎えた後でも、ラムダ式が生きているケースがあります。(例えば、ラムダ式を実行するスレッドを起動した関数がreturnした時など)
その時、その関数のローカル変数を参照キャプチャしていると、そのキャプチャした変数は無効なスタックを指しているのでなかなか痛いです。

ローカル変数を参照でreturnしたような状態になります。こちらは警告してくれる事が多いので比較的安全ですが、参照キャプチャは警告でないようです。

投稿2018/01/28 10:38

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Eki

2018/01/28 10:59

「1箇所からしか呼ばないことが確実だけど、関数にしないといけないような時」のためだけにその関数を他の場所でも使えるようにしてしまう、ということを防ぐ意味のメリットもありますね。スコープを縮めるというか。関数内関数は定義できないので... 個人的にはちょっとしたプログラムでクラスの配列を適当なメンバ変数基準にソートするために使ったり、 remove_if など述語を取る関数に使ったりします。
strike1217

2018/01/28 11:47

> そのメリットは、「関数を記述するのにイチイチmain関数の外にカーソルを移動しなくて済む。」 ほお・・・ そうですか!
strike1217

2018/01/28 11:48

「コンパイラの最適化が少しやりやすいということもあると思います。」 そうなんですか! 初めて聞きました。
strike1217

2018/01/28 11:50

C++のラムダ式は関数型プログラミングとは全く関係がないんですかね??
Chironian

2018/01/28 12:22 編集

なんとも。私自身は関数型プログラミングに興味はないので深く知らないです。 私は関数型プログラミング言語を触ったこと無いので理解が間違っているかもしれませんが、巷の幾つかの解説をみて単に副作用のない関数でプログラムするようなものと理解しています。それって、本質的にはグローバル変数とstaticローカル変数を使わないC言語なので、正直ぞっとして近寄りたくないと感じているのです。C++だけで手一杯というのもあります。
strike1217

2018/01/28 12:31

「本質的にはグローバル変数とstaticローカル変数を使わないC言語」 そのように考えればよろしいんですね。
Chironian

2018/01/28 12:36 編集

う~ん、ちょっと自信ありません。私はそのように理解したのでそれ以上調べていないのです。 私が見たいくつかの解説が流行りに乗っかっているだけの人の記事という可能性もあります。 実際に触っている人の意見を聞いた方が良いだろうと思います。
yumetodo

2018/01/28 12:45

すくなくともC++においてはlambda式の用途は既出の通りで、内部的にはただの関数オブジェクトなので、関数型プログラミングに対する理解は一ミリも必要ではありません
strike1217

2018/01/28 12:52

「関数型プログラミングに対する理解は一ミリも必要ではありません」 げげ!! そうなんですね!!
mkgrei

2018/01/28 12:53

理論上は遅延評価なので、実行するところではもう情報は出揃っていてグローバルに最適化できるんですけどね。
guest

0

あくまでも便利なもの程度の認識です。単純に使い手の好み、と言ってしまえばそれまでです。内部関数なり、基本構文なりで代用できるならば必要とまではいえません。

とはいえ、プログラムを簡素に記述でき、外部と名前重複の懸念がないというのは魅力的ではあります。今やプログラムの規模は膨大になってきているので、ソースを見やすくすることも技術の一つであるといえます。

以下、参考
さらにいうならば、例えば総合開発環境の高機能化への追従も社会通念上、事実上の必要事項であるわけで、一種の派閥や政治論にも波及するところです。使い手の好みの問題、といっても、複数人で作業する場合などでは決して簡単には割り切ることができないものだったりします。偉い人の好みというだけで、やり方が決定するということも多々ありえます。

以下、追記
関数型言語のお話もされているようなので、これについても述べます。関数型言語といえば、私はHaskellが代表として筆頭だと思っております。最大の理由は「変数を使用できないこと(初期値を変更できない)」です。(この制約のため、多くの処理を再帰によって記述することになります)

この制約のおかげでHaskellは単独で可能な処理が著しく制限される一方で、他の言語にないメリットがあります。「外界に影響を与えずにデータを変換する」ということに着目すれば下請けとして非常に有用であり、ラムダに通じるものであると考えております。

投稿2018/01/28 11:15

編集2018/01/28 13:16
HogeAnimalLover

総合スコア4830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/01/28 13:21

関数型で調べると、Haskellの名前はよく出てきますね。 自分は全く分かりませんが 「初期値を変更できない」というのはconstに相当しそうな考えですが・・・ 「外界に影響を与えずにデータを変換する」 ふむ? なんか新しい観点が出てきましたね。 C++も関数型プログラミングに関係がある・・・と考えられるわけですね!
guest

0

なおC++で関数型プログラミングをやろうとして時期尚早だったと諦めている検証例としては

さいきょうの関数型言語はC++だったのかもしれないという可能性は慎重に検討されねばならない - Qiita

があります。

投稿2018/01/29 09:19

yumetodo

総合スコア5850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2018/01/29 10:14

ああ! その記事は少しだけ目を通したことがあります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問