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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C++11

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

C++

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

Q&A

解決済

4回答

7007閲覧

ラムダ式を流用して多重ループから抜ける。有り?無し?

Chironian

総合スコア23272

C++11

C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

C++

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

0グッド

2クリップ

投稿2015/10/30 07:34

編集2015/10/30 13:52

C++で多重ループから抜ける時はいつも悩ましいです。
終了フラグを使う、gotoする、関数化するぐらいしか方法がないと思ってました。
しかし、C++11からラムダ式がサポートされ、これを使えば多重ループから抜けられることに気が付きました。(既に使っている人が結構いるような気がしますが、まだ見たことないです。)

C++

1#include <iostream> 2int main() 3{ 4 std::cout << "--- Start ---\n"; 5 [&]{ 6 for (int i=0; i < 5; ++i) { 7 for (int j=0; j < 5; ++j) { 8 std::cout << "[" << i << ", " << j << "]\n"; 9 if ((3 <= i) && (4 <= j)) { 10 std::cout << "end\n"; 11 return; 12 } 13 } 14 } 15 }(); 16 std::cout << "--- End ---\n"; 17}

関数化した方が見通しがよくなる時は関数化するので問題ないのですが、関数化すると見通しが悪くなることがあります。
そのような場合、どれが一番マシと思いますか?

①終了フラグを使う
②gotoを使う
③見通しが悪くなっても関数化する
④ラムダ式を使う
⑤その他(もし、あれば)

実際には、ループを抜けるのにreturn;では意味が異なりすぎて宜しくないので、マクロを使うと思います。

C++

1#include <iostream> 2 3// ラムダ式を使った多重ループbreak 4#define MULTI_BREAK_BEGIN [&] 5#define MULTI_BREAK return; 6#define MULTI_BREAK_END (); 7 8int main() 9{ 10 std::cout << "--- Start ---\n"; 11 MULTI_BREAK_BEGIN 12 { 13 for (int i=0; i < 5; ++i) { 14 for (int j=0; j < 5; ++j) { 15 std::cout << "[" << i << ", " << j << "]\n"; 16 if ((3 <= i) && (4 <= j)) { 17 std::cout << "end\n"; 18 MULTI_BREAK 19 } 20 } 21 } 22 } 23 MULTI_BREAK_END 24 std::cout << "--- End ---\n"; 25}

我ながら「なんだかな~」って感じますが、①②よりはマシな気がするので皆さんのご意見をお聞かせ下さい。


【追記】
皆さん、ご意見、ありがとうございました。
私自身も元々goto派なのでgotoの意見が多くてなんとなく嬉しかったです。

しょっちゅう出てくるようなパターンでもないですしね。

このご意見が私の中で一番響いたので、toki_tdさんの回答をベストアンサーにさせて頂きました。

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

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

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

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

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

guest

回答4

0

ベストアンサー

僕も②ですね。
ループをどうやって抜けるかはアルゴリズムでは重要ではなくてどういったときに抜けるかが主な問題のため
①はアルゴリズムに本来不要な確認事項を追加してしまうし、③は本末転倒で関数化の意味を失っているし
対のラベルが今の制御フローの終端にある goto EXIT_MULTI_LOOP が一番明快だと思います。

その場で呼ぶラムダ式だとラムダ式にした意味を"return"から読み解く必要があるし
マクロになっていると誰かからソースをもらって読むときマクロの正しさをまず確認して
頭の中では一度展開する必要があるので余計に身構えてしまうかな。
しょっちゅう出てくるようなパターンでもないですしね。

投稿2015/10/30 13:11

toki_td

総合スコア2850

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

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

Chironian

2015/10/30 13:39

>しょっちゅう出てくるようなパターンでもないですしね。 確かにそうですね。頻繁に使うパターンなら多少変な使い方でも慣れることができますが、滅多に使わないのに本来と異なる使い方をするとハマりますから。
guest

0

個人的には「②gotoを使う」一択ですね。

ソースコード上の配置を除けば、「④ラムダ式を使う」は本質的には「③見通しが悪くなっても関数化する」と同じですから、あとは個人やチームの好みで選べば良いと思います。

個人的にはマクロ版へは強い嫌悪感がありますが、目的によるでしょうね。

投稿2015/10/30 12:50

編集2015/10/30 12:52
yohhoy

総合スコア6191

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

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

Chironian

2015/10/30 13:46

>個人的にはマクロ版へは強い嫌悪感がありますが、目的によるでしょうね。 あは。記号だらけのラムダ式を更に分解したマクロなので自分でも書いてていくらなんでもあんまりだなって思ってました。このマクロ定義だけをみて意味が理解できる人は天才ですね。
guest

0

自分だったら、(これまた行儀は良くない部類かもしれませんが)5で「専用の例外を投げる」という手もあるかなと考えました。

投稿2015/10/30 08:11

maisumakun

総合スコア145183

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

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

Chironian

2015/10/30 10:34

回答、ありがとうございます。 なるほど例外は盲点でした。 しかも、 struct MultiBreak {}; #define MULTI_BREAK_BEGIN try #define MULTI_BREAK throw MultiBreak(); #define MULTI_BREAK_END catch(MultiBreak&){}; としたら、見かけ上同じですね。 ただ、正常処理で例外を投げることになるので、性能はちょっと気になります。
maisumakun

2015/10/31 00:05

RubyではStopIterationなんていう、「ループを抜け出す」専用の例外すら処理系標準であるので、C++的な発想ではないかもしれませんが1つの手段かなと思いました。
Chironian

2015/10/31 03:37

確かに1つの手段ですね。 更にtyr-catch構文なら、ループを途中で抜けた時と最後まで回った時を綺麗に分けて処理できますし。 ただ、C++では例外処理は重いとよく聞きます。 本当にそうなのか実際に確認したことはなかったので、良い機会と思い調べてみました。 cout出力を削除して100,000回のループを、MinGW 64bitで-std=c++11のみオプションを付けてビルドして計測したところ、例外の時は149mSec程度、ラムダ式の時は3.5mSec程度とかなり差が付きました。 計測はWindows7 64bitでバフォーマンスカウンタを使ってます。 巷で言われているように、やはりC++の場合は、例外は本当に例外的な時にだけ投げた方が良さそうです。 maisumakunさんがおっしゃるようにC++でもループを抜けるための専用の「例外」のような構文で使える高速な仕組みがあると良いのですけどね。
guest

0

無名関数のこういう使い方は思いつかなかったので、発想としては面白いと思いました。
しかし採用するかと言われると。
この記法の目的はリーダビリティの改善のはずです。ではこれ自身はリーダブルか? そうだとは断言できないからこそこうして質問なさっていることとは思いますが、私はリーダブルでないと思います。

リーダビリティを損ねている理由は「道具の一般的な目的と違う使い方」にあるかと思います。
無名関数は一般的にコールバック、「渡した先の都合で呼ばれる(かもしれない)」処理の書き方です。アルゴリズムを書き下す道具ではない、そこが読む側の自然な理解を妨げると。

ループの深いネストとそこからの脱出が避けられないものであるなら、その中でリーダビリティを確保するのはgotoか良い命名のフラグですかね。

投稿2015/10/30 13:06

yuba

総合スコア5568

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

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

Chironian

2015/10/30 13:40

>リーダビリティを損ねている理由は「道具の一般的な目的と違う使い方」にあるかと思います。 ですね。いきなり出てきたら、絶対読めない自信が自分でもあります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問