イテレータ 解答ページ | Programming Place Plus 新C++編

トップページ新C++編イテレータ

このページの概要

このページは、練習問題の解答例や解説のページです。



解答・解説

問題1 (確認★)

次の各変数の型は何になりますか?(v は std::vector<int> の変数とします)。

  1. auto x = 0;
  2. auto x = 0.0;
  3. auto x = 3.5 + 10;
  4. auto x = std::begin(v);
  5. auto x = std::cbegin(v);
  6. auto x = std::rbegin(v);
  7. auto x = std::crbegin(v);
  8. auto x = v;


変数を宣言するとき、型名を auto にした場合には、初期化子から型が自動的に決定されます(本編解説)。

1番は、0 を与えています。整数の 0 は int型なので、x は int型です。

2番は、0.0 を与えています。これは double型なので、x は double型です。

3番は、3.5 + 10 を与えています。3.5 は double型、10 は int型です。整数型と浮動小数点型による計算の結果は、浮動小数点型のほうに合わせられるため、ここでは double型になります。このルールは「浮動小数点数」のページで解説しました。x は double型です。

4番は、std::begin(v) を与えています。std::begin関数が返すものは、対象のデータ構造のイテレータ型です(本編解説)。v は std::vector<int> なので、std::vector<int>::iterator型になります。

5番は、std::cbegin(v) を与えています。std::cbegin関数は、constイテレータを返しますから、x は std::vector<int>::const_iterator型です(本編解説)。

6番は、std::rbegin(v) を与えています。std::rbegin関数は、逆イテレータを返しますから、x は std::vector<int>::reverse_iterator型です(本編解説)。

7番は、std::crbegin(v) を与えています。std::crbegin関数は、const逆イテレータを返しますから、x は std::vector<int>::const_reverse_iterator型です(本編解説)。

8番は、v を与えています。v は std::vector<int> ですから、x は std::vector<int> です。これは std::vector のコピーによる初期化です(「std::vector」のページを参照)。

問題2 (基本★)

標準入力から整数を5つ入力させ、std::vector に格納するプログラムを作成してください。

この問題は、「std::vector」のページの練習問題でも出題しました。今回は、イテレータを用いるようにしてみてください。


たとえば次のようになります。

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v(5);

    for (auto it = std::begin(v); it != std::end(v); ++it) {
        std::cout << "Please enter the integer.\n";
        std::cin >> *it;
    }

    for (int e : v) {
        std::cout << e << "\n";
    }
}

実行結果:

Please enter the integer.
3  <-- 入力した整数
Please enter the integer.
4  <-- 入力した整数
Please enter the integer.
5  <-- 入力した整数
Please enter the integer.
6  <-- 入力した整数
Please enter the integer.
7  <-- 入力した整数
3
4
5
6
7

結果を出力するところは範囲for文にしています。先頭から末尾まで順にアクセスすることだけが目的なのであれば、イテレータを使って書くより、範囲for文のほうが簡潔です。もしイテレータで書くなら、次のようになります。要素の書き換えを行わないので、constイテレータが使えます(本編解説)。

    for (auto it = std::cbegin(v); it != std::cend(v); ++it) {
        std::cout << *it << "\n";
    }

問題3 (基本★)

std::vector の内容を constイテレータを使った方法で出力する for文を書き、何番目の要素であるかをあらわす連番もセットで出力されるようにしてください。

たとえば、次のようなイメージです。

0: 5
1: 8
2: 3
3: 10
4: 6


イテレータと添字の両立を図るという感じの問題です。イテレータと添字の変数を別個に用意します。

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v {5, 8, 3, 10, 6};

    int index {0};
    for (auto it = std::cbegin(v); it != std::cend(v); ++it) {
        std::cout << index << ": " << *it << "\n";
        ++index;
    }
}

実行結果:

0: 5
1: 8
2: 3
3: 10
4: 6

【上級】++index; は、for文にまとめてしまうこともできます。for (auto it = std::cbegin(v); it != std::cend(v); ++it, ++index)

問題4 (基本★★)

標準入力から文字列を5つ入力させ、std::vector に格納したあと、入力された順番とは逆の順番で出力するプログラムを作成してください。

この問題は、「std::vector」のページの練習問題でも出題しました。今回は、イテレータを用いるようにしてみてください。


たとえば次のようになります。

#include <iostream>
#include <iterator>
#include <string>
#include <vector>

int main()
{
    std::vector<std::string> v(5);

    for (auto it = std::begin(v); it != std::end(v); ++it) {
        std::cout << "Please enter the string.\n";
        std::cin >> *it;
    }

    for (auto it = std::crbegin(v); it != std::crend(v); ++it) {
        std::cout << *it << "\n";
    }
}

実行結果:

Please enter the string.
aaa  <-- 入力した文字列
Please enter the string.
bbbb  <-- 入力した文字列
Please enter the string.
ccccc  <-- 入力した文字列
Please enter the string.
dddd  <-- 入力した文字列
Please enter the string.
eee  <-- 入力した文字列
eee
dddd
ccccc
bbbb
aaa

標準入力から受け取る部分は、問題2と同じです。問題2では整数(int型)を扱っていたのが、今回は文字列(std::string型)に変わっていますが、コードのかたちはまったく変化ありません。

逆の順番で出力するためには、std::vector を逆方向に辿りながら値を出力していけばいいです。そのために、逆イテレータが使えます(本編解説)。今回は、要素の書き換えが不要なので、const逆イテレータにできます(本編解説)。

std::vector」のページの練習問題のときには、添字を使って for (int i = element_count - 1; i >= 0; --i) というコードを書きました。添字を持ち出すと、範囲外アクセスなどの事故を起こす可能性がありますし、一見して何をしているかわかりづらいこともあります。逆イテレータを使った for (auto it = std::crbegin(v); it != std::crend(v); ++it) というコードは定型文のようなものなので、知ってさえいればほとんど間違えようがありません。

問題5 (応用★★★)

問題4のプログラムを改造して、文字列それぞれについても逆順(“Hello” なら “olleH”)に出力するようにしてください。


まず、問題4のとき、出力部分のコードはこうなっていました。

    for (auto it = std::crbegin(v); it != std::crend(v); ++it) {
        std::cout << *it << "\n";
    }

イテレータit が指しているものが std::string型の値であることを理解しておきましょう(変数v は std::vector<std::string> なので、要素の型は std::string型)。

std::string にもイテレータが適用できるので、逆イテレータを使えば、文字列の末尾側から先頭に向かって、各文字にアクセスできます。そこで、std::vector のイテレータit が指し示している std::string に対して、もう1つ逆イテレータを作ります。std::vector を逆順に辿る逆イテレータで for文を構築しつつ、さらに std::string を逆順に辿る逆イテレータによる for文を構築するという、二重ループ構造になります。

#include <iostream>
#include <iterator>
#include <string>
#include <vector>

int main()
{
    std::vector<std::string> v(5);

    for (auto it = std::begin(v); it != std::end(v); ++it) {
        std::cout << "Please enter the string.\n";
        std::cin >> *it;
    }

    for (auto it = std::crbegin(v); it != std::crend(v); ++it) {
        for (auto it2 = std::crbegin(*it); it2 != std::crend(*it); ++it2) {
            std::cout << *it2;
        }
        std::cout << "\n";
    }
}

実行結果:

Please enter the string.
a  <-- 入力した文字列
Please enter the string.
ab  <-- 入力した文字列
Please enter the string.
aabb  <-- 入力した文字列
Please enter the string.
aabbcc  <-- 入力した文字列
Please enter the string.
aaabbbccc  <-- 入力した文字列
cccbbbaaa
ccbbaa
bbaa
ba
a



参考リンク



更新履歴




はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る