R - 2.01.ループの書き方と範囲for文 Editorial /

Time Limit: 0 msec / Memory Limit: 0 KB

前のページ | 次のページ

キーポイント

  • ループ処理を書くときの3ステップ
  • 1.ループを使わずに書く
  • 2.パターンを見つける
  • 3.ループで書き直す
  • 配列の要素を取り出しながらループする範囲for文
for (配列の要素の型 変数名 : 配列変数) {
  // 各要素に対する処理
}

ループの書き方

1.10, 1.11ではwhile、forといったループ構文を紹介しました。

whileやforなどのループ構文は非常に重要です。しかし、文法を理解しても「ループで処理を書く」ということは慣れないと難しいかもしれません。

うまくループ処理が書けない時は、以下の手順でプログラムを書くのが良いでしょう。

  1. ループを使わないで書く
  2. パターンを見つける
  3. ループで書き直す

これを使って次の問題を解いてみます。

例題

整数aと5個の整数x_1, x_2, x_3, x_4, x_5が与えられます。
5個の整数のうちaと等しいものの個数をfor文を使って求めてください。

入力

a
x_1 x_2 x_3 x_4 x_5

出力

aと等しいものの個数

入力例

3
1 3 2 5 3

出力例

2

次のプログラムを元に説明していきます。

#include <bits/stdc++.h>
using namespace std;

int main() {
  int a;
  cin >> a;
  vector<int> data(5);
  for (int i = 0; i < 5; i++) {
    cin >> data.at(i);
  }

  // ここにプログラムを追記
}

解答例

まずはループを使わないでプログラムを書きます。

#include <bits/stdc++.h>
using namespace std;

int main() {
  int a;
  cin >> a;
  vector<int> data(5);
  for (int i = 0; i < 5; i++) {
    cin >> data.at(i);
  }

  // 答えを保持する変数
  int answer = 0;

  if (data.at(0) == a) {
    answer++;
  }

  if (data.at(1) == a) {
    answer++;
  }

  if (data.at(2) == a) {
    answer++;
  }

  if (data.at(3) == a) {
    answer++;
  }

  if (data.at(4) == a) {
    answer++;
  }

  cout << answer << endl;
}

ループで書けそうなパターンが見つからないか探してみると、次のパターンが続いていることがわかります。

if (data.at(数値) == a) {
  answer++;
}

これを元に、ループを使って書き直してみましょう。

#include <bits/stdc++.h>
using namespace std;

int main() {
  int a;
  cin >> a;
  vector<int> data(5);
  for (int i = 0; i < 5; i++) {
    cin >> data.at(i);
  }

  // 答えを保持する変数
  int answer = 0;

  for (int i = 0; i < 5; i++) {

    if (data.at(i) == a) {
      answer++;
    }

  }

  cout << answer << endl;
}
入力
3
1 3 2 5 3
実行結果
2

範囲for文

配列の全ての要素に対して何かしらの処理を行ないたいとき、for文を用いて書くことができました。

例えば「配列の全ての要素を出力する」処理は次のように書くことができます。

#include <bits/stdc++.h>
using namespace std;

int main() {
  vector<int> a = {1, 3, 2, 5};
  for (int i = 0; i < a.size(); i++) {
    cout << a.at(i) << endl;
  }
}
実行結果
1
3
2
5

C++には配列の要素に対する処理を簡潔に書くことができる範囲for文という構文が用意されています。

範囲for文を用いると上のプログラムは次のように書き直せます。

#include <bits/stdc++.h>
using namespace std;

int main() {
  vector<int> a = {1, 3, 2, 5};
  for (int x : a) {
    cout << x << endl;
  }
}
実行結果
1
3
2
5

この例では、「配列変数aから要素を1つ取り出してxという変数にコピー→xの値を出力→次の要素をxにコピー→xの値を出力→…」 のように動作します。すべての要素を取り出し終わるとループを抜けます。

範囲for文は基本的に次の構文です。

for (配列の要素の型 変数名 : 配列変数) {
  // 各要素に対する処理
}

全ての要素を取り出し終わるとループを抜けます。

範囲for文でも、for文・while文と同様にbreakcontinueを使うことができます。
いずれもfor文・while文のときと同じ動作(breakでループを抜け、continueで次のループまで処理をスキップ)をします。

#include <bits/stdc++.h>
using namespace std;

int main() {
  vector<int> a = {1, 3, 1, 2, 5, 10};
  for (int x : a) {
    if (x == 1) {
      continue;
    }

    if (x == 5) {
      break;
    }

    cout << x << endl;
  }
}
実行結果
3
2

範囲for文はコンテナと呼ばれるデータ型に対して使うことができます。
配列はコンテナの一種です。 その他にも文字列型(string型)はコンテナの一種なので、範囲for文を用いることができます。
string型の変数に対して、1文字ずつ処理したい場合に便利です。

#include <bits/stdc++.h>
using namespace std;

int main() {
  string str = "hello";
  for (char c : str) {
    if (c == 'l') {
      c = 'L';
    }
    cout << c;
  }
  cout << endl;
}
実行結果
heLLo

細かい話

ループ構文の使い分け

ループ構文には1.10で扱ったwhile文、1.11で扱ったfor文、今回紹介した範囲for文がありますが、どのように使い分ければ良いのでしょうか。

for文は1.11で見たように「N回処理する」というようなパターンをwhile文より短く書くための構文でした。 今回紹介した範囲for文は配列に対する処理をfor文よりも簡潔に書くための構文でした。 よって、以下のように使い分けると良いでしょう。

  • 配列の全ての要素に対する処理を行なう場合 → 範囲for文
  • それ以外で一定回数繰り返し処理する場合 → for文
  • それ以外の場合 → while文

ただし、例えば配列の要素に対する処理でも、範囲for文を用いるよりもfor文やwhile文を用いた方が簡潔に書ける場合もあるので、 必ずしも上のように使い分ける必要はありません。

while文が適しているケース

「整数Nがあるとき、Nが2で最大で何回割り切れるかを求める」という処理を考えます。 この処理は配列の要素に対する処理ではありませんし、具体的に何回処理を繰り返せば良いのかということも分かりません。 この処理にはwhile文が適しているでしょう。

次のサンプルプログラムは、入力で与えられた整数Nが2で割り切れる回数を出力するプログラムです。

#include <bits/stdc++.h>
using namespace std;

int main() {
  int N;
  cin >> N;

  int count = 0;
  while (N > 0) {
    // 2で割り切れなければループを抜ける
    if (N % 2 > 0) {
      break;
    }
    N = N / 2;
    count++;
  }
  cout << count << endl;
}
入力1
8
実行結果1
3
入力2
5
実行結果1
0

問題

リンク先の問題を解いてください。