サーチ…
前書き
ループ文は、条件が満たされるまで繰り返し文のグループを実行します。 C ++には、for、while、do ... whileの3種類のプリミティブループがあります。
構文
- while( 条件 ) ステートメント 。
- while( 式 )while ステートメント
- for( init-statement ; 条件 ; expression ) ステートメント ;
- for( range-for-range-declaration : for-range-initializer ) ステートメント 。
- 休憩。
- 持続する ;
備考
一般に手書きループよりもalgorithm
呼び出しが好ましい。
アルゴリズムが既に何か(あるいは非常に似ている)を望むならば、アルゴリズム呼び出しはより明確になり、しばしばより効率的でエラーの起こりにくいものになります。
非常にシンプルなループが必要な場合(ただし、アルゴリズムを使用している場合は、バインダーとアダプタの混乱が必要です)、ループを作成してください。
範囲ベースのFor
for
ループは、数値インデックスを使用せずに、またはイテレータに直接アクセスすることなく、イテレータベースの範囲の要素を反復処理するために使用できます。
vector<float> v = {0.4f, 12.5f, 16.234f};
for(auto val: v)
{
std::cout << val << " ";
}
std::cout << std::endl;
これは、現在の要素の値を取得するval
で、 v
すべての要素を繰り返し処理します。次の文:
for (for-range-declaration : for-range-initializer ) statement
次のものと同等です。
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr, __end = end-expr;
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr;
auto __end = end-expr; // end is allowed to be a different type than begin in C++17
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
この変更は、Ranges TSのC ++での計画サポートのために導入されました20。
この場合、ループは次のようになります。
{
auto&& __range = v;
auto __begin = v.begin(), __end = v.end();
for (; __begin != __end; ++__begin) {
auto val = *__begin;
std::cout << val << " ";
}
}
auto val
は値の型を宣言することに注意してください。値の型は範囲に格納された値のコピーになります(私たちはイテレータからコピーを初期化しています)。範囲に格納された値が高価な場合は、 const auto &val
を使用することができます。 auto
を使う必要もありません。範囲の値型から暗黙的に変換可能である限り、適切な型名を使用できます。
イテレータにアクセスする必要がある場合は、range-based forはあなたを助けることはできません(少なくとも努力する必要はありません)。
それを参照したい場合は、そうすることができます:
vector<float> v = {0.4f, 12.5f, 16.234f};
for(float &val: v)
{
std::cout << val << " ";
}
const
コンテナを持っている場合、 const
参照を反復することができます:
const vector<float> v = {0.4f, 12.5f, 16.234f};
for(const float &val: v)
{
std::cout << val << " ";
}
シーケンス反復子がプロキシオブジェクトを返し、そのオブジェクトをconst
方法で操作する必要がある場合、転送参照を使用します。注:あなたのコードの読者を混乱させる可能性が高いでしょう。
vector<bool> v(10);
for(auto&& val: v)
{
val = true;
}
範囲ベースのfor
提供される "range"タイプは、次のいずれかになります。
言語配列:
float arr[] = {0.4f, 12.5f, 16.234f}; for(auto val: arr) { std::cout << val << " "; }
動的配列の割り当てはカウントされないことに注意してください。
float *arr = new float[3]{0.4f, 12.5f, 16.234f}; for(auto val: arr) //Compile error. { std::cout << val << " "; }
メンバ関数
begin()
とend()
を持つ型で、型の要素にイテレータを返します。標準ライブラリコンテナは修飾されますが、ユーザ定義型も使用できます。struct Rng { float arr[3]; // pointers are iterators const float* begin() const {return &arr[0];} const float* end() const {return &arr[3];} float* begin() {return &arr[0];} float* end() {return &arr[3];} }; int main() { Rng rng = {{0.4f, 12.5f, 16.234f}}; for(auto val: rng) { std::cout << val << " "; } }
非会員有する任意のタイプの
begin(type)
及びend(type)
に基づいて、引数依存ルックアップを介して見出さできる機能type
。これは、クラスタイプ自体を変更することなく範囲タイプを作成するのに便利です:namespace Mine { struct Rng {float arr[3];}; // pointers are iterators const float* begin(const Rng &rng) {return &rng.arr[0];} const float* end(const Rng &rng) {return &rng.arr[3];} float* begin(Rng &rng) {return &rng.arr[0];} float* end(Rng &rng) {return &rng.arr[3];} } int main() { Mine::Rng rng = {{0.4f, 12.5f, 16.234f}}; for(auto val: rng) { std::cout << val << " "; } }
ループの場合
for
ループは、ループcondition
が真である間に、 loop body
ステートメントを実行します。ループinitialization statement
が正確に1回実行される前。各サイクルの後、 iteration execution
部分が実行される。
for
ループは次のように定義されます。
for (/*initialization statement*/; /*condition*/; /*iteration execution*/)
{
// body of the loop
}
プレースホルダーステートメントの説明:
-
initialization statement
:このステートメントは、for
ループの開始時に1回だけ実行さfor
ます。int i = 0, a = 2, b = 3
など、1つの型の複数の変数の宣言を入力できます。これらの変数は、ループの有効範囲内でのみ有効です。同じ名前のループの前に定義された変数は、ループの実行中に隠されます。 -
condition
:このステートメントは、各ループ本体の実行の前に評価され、ループが評価されると、ループを中止しfalse
。 -
iteration execution
:ない限り、この文は、先に次の条件評価の、ループ本体の後に実行されますfor
ループは( 体内に中止されたbreak
、goto
、return
またはスローされた例外)。a++, b+=10, c=b+a
など、iteration execution
部分に複数のステートメントを入力できます。
大まかな同等for
のように書き換えるループ、 while
ループがあります。
/*initialization*/
while (/*condition*/)
{
// body of the loop; using 'continue' will skip to increment part below
/*iteration execution*/
}
for
ループを使用する最も一般的なケースは、特定の回数だけステートメントを実行することです。たとえば、次の点を考慮してください。
for(int i = 0; i < 10; i++) {
std::cout << i << std::endl;
}
有効なループもあります:
for(int a = 0, b = 10, c = 20; (a+b+c < 100); c--, b++, a+=c) {
std::cout << a << " " << b << " " << c << std::endl;
}
ループの前に宣言された変数を隠す例を次に示します。
int i = 99; //i = 99
for(int i = 0; i < 10; i++) { //we declare a new variable i
//some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 99
しかし、すでに宣言されている変数を使用して非表示にしたくない場合は、宣言部を省略します。
int i = 99; //i = 99
for(i = 0; i < 10; i++) { //we are using already declared variable i
//some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 10
ノート:
- 初期化ステートメントとインクリメントステートメントは、条件ステートメントと無関係な操作を実行することができます。しかし、可読性の理由から、ループに直接関係する操作のみを実行することがベストプラクティスです。
- 初期化ステートメントで宣言された変数は、
for
ループのスコープ内でのみ表示され、ループの終了時に解放されます。 -
initialization statement
宣言された変数は、ループ中に変更できること、およびcondition
チェックされた変数も変更できることを忘れないでください。
0から10までカウントするループの例:
for (int counter = 0; counter <= 10; ++counter)
{
std::cout << counter << '\n';
}
// counter is not accessible here (had value 11 at the end)
コード断片の説明:
-
int counter = 0
は変数counter
をint counter = 0
初期化します(この変数はfor
ループ内でのみ使用できます)。 -
counter <= 10
は、counter
が10以下かどうかを調べるブール条件です。true
場合、ループが実行されます。false
場合、ループは終了します。 -
++counter
は、次の条件チェックの前にcounter
の値を1ずつインクリメントするインクリメント演算です。
すべてのステートメントを空のままにすると、無限ループを作成できます。
// infinite loop
for (;;)
std::cout << "Never ending!\n";
上記のwhile
ループに相当するものは次のとおりです。
// infinite loop
while (true)
std::cout << "Never ending!\n";
ただし、 break
、 goto
、またはreturn
文を使用するか、または例外をスローすることによって無限ループを残すことができます。
<algorithm>
ヘッダーを使用せずにSTLコレクション(たとえばvector
)からすべての要素を反復処理する次の一般的な例は次のとおりです。
std::vector<std::string> names = {"Albert Einstein", "Stephen Hawking", "Michael Ellis"};
for(std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
std::cout << *it << std::endl;
}
whileループ
while
ループは、指定された条件がfalse
評価されるまで、ステートメントを繰り返し実行しfalse
。この制御文は、コードのブロックが何回実行されるかが事前に分かっていない場合に使用されます。
たとえば、0から9までのすべての数値を出力するには、次のコードを使用します。
int i = 0;
while (i < 10)
{
std::cout << i << " ";
++i; // Increment counter
}
std::cout << std::endl; // End of line; "0 1 2 3 4 5 6 7 8 9" is printed to the console
C ++ 17以来、最初の2つのステートメントを組み合わせることができます
while (int i = 0; i < 10)
//... The rest is the same
無限ループを作成するには、次の構文を使用できます。
while (true)
{
// Do something forever (however, you can exit the loop by calling 'break'
}
while
ループの別の変種、つまりdo...while
構文があります。詳細については、 do-whileループの例を参照してください。
条件変数の宣言
for
ループとwhile
ループの条件では、オブジェクトを宣言することもできます。このオブジェクトは、ループの終わりまでスコープ内にあるとみなされ、ループの各繰り返しを通して保持されます。
for (int i = 0; i < 5; ++i) {
do_something(i);
}
// i is no longer in scope.
for (auto& a : some_container) {
a.do_something();
}
// a is no longer in scope.
while(std::shared_ptr<Object> p = get_object()) {
p->do_something();
}
// p is no longer in scope.
しかし、 do...while
ループで同じことをすることは許されません。代わりに、ループの前に変数を宣言し、ループが終了した後に変数をスコープから外したい場合は、変数とループの両方をローカルスコープ内に囲みます(オプション)。
//This doesn't compile
do {
s = do_something();
} while (short s > 0);
// Good
short s;
do {
s = do_something();
} while (s > 0);
これは、 計算書の一部ためであるdo...while
ループは(ループの体) の発現部分 (前に評価されてwhile
)に達しており、従って、 発現のいずれかの宣言が最初の反復中に表示されなくなりますループ。
Do-whileループ
条件はありません開始時に、各サイクルの終わりでチェックされていることを除いて、DO-whileループは、whileループと非常によく似ています。したがって、ループは少なくとも1回は実行されることが保証されます。
次のコードでは、最初の繰り返しの最後に条件がfalse
と評価されるため、 0
が出力されます。
int i =0;
do
{
std::cout << i;
++i; // Increment counter
}
while (i < 0);
std::cout << std::endl; // End of line; 0 is printed to the console
注: while(condition);
最後にセミコロンを忘れないでくださいwhile(condition);
これはdo-while構文で必要とされます。
do-whileループとは対照的に、最初の反復の開始時には条件がfalse
と評価されるため、以下は何も印刷しません。
int i =0;
while (i < 0)
{
std::cout << i;
++i; // Increment counter
}
std::cout << std::endl; // End of line; nothing is printed to the console
注: whileループは、 break
、 goto
、またはreturn
ステートメントを使用して条件が偽になることなく終了することができます。
int i = 0;
do
{
std::cout << i;
++i; // Increment counter
if (i > 5)
{
break;
}
}
while (true);
std::cout << std::endl; // End of line; 0 1 2 3 4 5 is printed to the console
簡単なdo-whileループは、独自のスコープを必要とするマクロを書き込むためにも使用されることがあります(その場合、マクロの定義から後続のセミコロンは省略され、ユーザーが提供する必要があります)。
#define BAD_MACRO(x) f1(x); f2(x); f3(x);
// Only the call to f1 is protected by the condition here
if (cond) BAD_MACRO(var);
#define GOOD_MACRO(x) do { f1(x); f2(x); f3(x); } while(0)
// All calls are protected here
if (cond) GOOD_MACRO(var);
ループ制御文:中断し続ける
ループ制御ステートメントは、通常のシーケンスから実行フローを変更するために使用されます。実行がスコープを離れると、そのスコープで作成されたすべての自動オブジェクトが破棄されます。 break
とcontinue
はループ制御文です。
break
ステートメントは、これ以上考慮することなくループを終了します。
for (int i = 0; i < 10; i++)
{
if (i == 4)
break; // this will immediately exit our loop
std::cout << i << '\n';
}
上記のコードが出力されます:
1
2
3
continue
文はループをすぐに終了するのではなく、残りのループ本体をスキップしてループの先頭に移動します(条件のチェックを含む)。
for (int i = 0; i < 6; i++)
{
if (i % 2 == 0) // evaluates to true if i is even
continue; // this will immediately go back to the start of the loop
/* the next line will only be reached if the above "continue" statement
does not execute */
std::cout << i << " is an odd number\n";
}
上記のコードが出力されます:
1 is an odd number
3 is an odd number
5 is an odd number
このような制御フローの変更は、人間が容易に理解することが困難な場合があるため、 break
やcontinue
は控えめに使用されます。通常、より簡単な実装で読みやすく理解しやすくなります。たとえば、上のbreak
持つ最初のfor
ループは次のように書き直されるかもしれません:
for (int i = 0; i < 4; i++)
{
std::cout << i << '\n';
}
continue
の2番目の例は、次のように書き直すことcontinue
できます。
for (int i = 0; i < 6; i++)
{
if (i % 2 != 0) {
std::cout << i << " is an odd number\n";
}
}
範囲 - サブ範囲にわたって
範囲ベースのループを使用すると、範囲ベースのforループの対象となるプロキシオブジェクトを生成することによって、特定のコンテナまたは他の範囲のサブパートをループすることができます。
template<class Iterator, class Sentinel=Iterator>
struct range_t {
Iterator b;
Sentinel e;
Iterator begin() const { return b; }
Sentinel end() const { return e; }
bool empty() const { return begin()==end(); }
range_t without_front( std::size_t count=1 ) const {
if (std::is_same< std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category >{} ) {
count = (std::min)(std::size_t(std::distance(b,e)), count);
}
return {std::next(b, count), e};
}
range_t without_back( std::size_t count=1 ) const {
if (std::is_same< std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category >{} ) {
count = (std::min)(std::size_t(std::distance(b,e)), count);
}
return {b, std::prev(e, count)};
}
};
template<class Iterator, class Sentinel>
range_t<Iterator, Sentinel> range( Iterator b, Sentinal e ) {
return {b,e};
}
template<class Iterable>
auto range( Iterable& r ) {
using std::begin; using std::end;
return range(begin(r),end(r));
}
template<class C>
auto except_first( C& c ) {
auto r = range(c);
if (r.empty()) return r;
return r.without_front();
}
今私達はできる:
std::vector<int> v = {1,2,3,4};
for (auto i : except_first(v))
std::cout << i << '\n';
プリントアウトする
2
3
4
for
ループのfor(:range_expression)
部分で生成される中間オブジェクトは、 for
ループの開始時に期限切れになることに注意してください。