先頭へ戻る

関数を作る 解答ページ | Programming Place Plus 新C++編

Programming Place Plus トップページ新C++編関数を作る

先頭へ戻る

このページの概要

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



解答・解説

問題1 (基本★)

次のプログラムの実行結果はどうなりますか?

#include <iostream>

void f1(int n);
void f2(int n1, int n2);

int main()
{
    f1(1);
    f2(10, 20);
}

void f1(int n)
{
    f2(n, n + 1);
}

void f2(int n1, int n2)
{
    std::cout << n1 << ", " << n2 << "\n";
}


関数がいくつあって、どこに書かれていようとも、プログラムの開始は main関数からというルールは変わりません。そのため、まずは f1(1); が実行され、f1関数に進みます。このとき実引数の 1 が渡され、f1関数の仮引数n の値になります(本編解説)。

f1関数の定義をみると、f2(n, n + 1); とあります。したがって今度は f2関数へ進みます。このとき、実引数の nn + 1 を計算した値が、f2関数の仮引数n1、n2 の値になります。n の値は 1 なので、1 と 2 が渡されます。

f2関数は n1 と n2 の値を出力しています。それぞれ 1 と 2 ですから、出力結果はこうです。

1, 2

f2関数にはこれ以上処理がありませんから、呼び出し元に戻ります(本編解説)。戻った先の f1関数にもこれ以上の処理がありませんから、さらに呼び出し元に戻ります。戻り先は main関数内の f1(1); の直後です。f2(10, 20); があるので、次は f2関数に進みます。今回の実引数は 1020 です。

f2関数では n1 と n2 の値を出力します。今回は 1020 なので、出力結果はこうです。

10, 20

f2関数にはこれ以上処理がありませんから、呼び出し元に戻ります。今回は main関数から直接来たので、戻り先も main関数です。main関数にはこれ以上処理がありませんから、これで main関数も終了となり、プログラムの実行が終わります。

最終的に出力結果はこうです。

1, 2
10, 20

このような処理の流れは、Visual Studio のステップ実行の機能を使って確認できます。「Visual Studio編>ステップ実行」のページで解説している、ステップインを使ってみるといいでしょう。

問題2 (基本★★)

std::vector<int> の要素の値を出力する関数を作成してください。すべての値を1行に、, で区切りながら出力するようにします。

たとえば、要素が 1, 2, 3, 4, 5 であれば、次のように出力します。

1, 2, 3, 4, 5


このような実行結果を得るプログラムは意外とやっかいです。というのも、末尾の要素の後ろには , を付けたくないため、どうしてもこの部分を特別扱いしたコードを書かなければならないためです。

色々とやりようはありますが、たとえば次のように書けます。

#include <iostream>
#include <vector>

void print_int_vector(const std::vector<int>& vec);

int main()
{
    std::vector<int> v1 {1, 2, 3, 4, 5};
    std::vector<int> v2 {};
    std::vector<int> v3(3, 7);

    print_int_vector(v1);
    print_int_vector(v2);
    print_int_vector(v3);
}

void print_int_vector(const std::vector<int>& vec)
{
    bool isFirst = true;

    for (auto e : vec) {
        if (!isFirst) {
            std::cout << ", ";
        }
        std::cout << e;
        isFirst = false;
    }
    std::cout << "\n";
}

実行結果:

1, 2, 3, 4, 5

7, 7, 7

最初の要素のときにだけ true になる変数isFirst を用意して、最初の要素以外のときにだけ , を出力するようにしています。

コードが書きづらいですし、無駄も大きくなるため、出力の形を工夫することも考えていいでしょう。たとえば、末尾に , が付くことを許していいかもしれませんし、単に空白文字で区切るようにすれば、末尾に空白が出力されていても気付かれないかもしれません。空白区切りなら次のように簡単に書けます。

void print_int_vector(const std::vector<int>& vec)
{
    for (auto e : vec) {
        std::cout << e << " ";
    }
    std::cout << "\n";
}

実行結果:

1 2 3 4 5 
 
7 7 7 

問題3 (応用★★)

std::vector<int> に複数の要素を追加する関数を作成してください。次のように呼び出せるようにしてください。

std::vector<int> v {};
push_back_values(v, {0, 1, 2, 3, 4});


push_back_values関数の1つ目の実引数は v であり、これは std::vector<int>型です。要素を追加する関数だということは、書き換えが必要ということですから、1つ目の仮引数の型は std::vector<int>& だということになります(本編解説)。

2つ目の実引数は {0, 1, 2, 3, 4} で、これは要素が int型の初期化子リストですから、2つ目の仮引数は std::initializer_list<int> ということになります(本編解説)。

したがって、次のように書けます。

#include <initializer_list>
#include <iostream>
#include <vector>

void push_back_values(std::vector<int>& vec, std::initializer_list<int> list);
void print_int_vector(const std::vector<int>& vec);

int main()
{
    std::vector<int> v {};
    push_back_values(v, {0, 1, 2, 3, 4});
    print_int_vector(v);
}

void push_back_values(std::vector<int>& vec, std::initializer_list<int> list)
{
    for (auto e : list) {
        vec.push_back(e);
    }
}

void print_int_vector(const std::vector<int>& vec)
{
    bool isFirst = true;

    for (auto e : vec) {
        if (!isFirst) {
            std::cout << ", ";
        }
        std::cout << e;
        isFirst = false;
    }
    std::cout << "\n";
}

実行結果:

0, 1, 2, 3, 4

print_int_vector関数は、問題② で作ったものを流用しています。

問題4 (応用★★)

std::vector<int> の要素から、指定の値を探して、指定の値に置き換える関数を作成してください。


たとえば次のように書けます。

#include <iostream>
#include <vector>

void replace_int_vector_value(std::vector<int>& vec, int target_value, int new_value);
void print_int_vector(const std::vector<int>& vec);

int main()
{
    std::vector<int> v1 {1, 2, 2, 3, 2, 4};
    std::vector<int> v2 {};

    replace_int_vector_value(v1, 2, 10);
    replace_int_vector_value(v2, 2, 10);

    print_int_vector(v1);
    print_int_vector(v2);
}

void replace_int_vector_value(std::vector<int>& vec, int target_value, int new_value)
{
    for (auto it = std::begin(vec); it != std::end(vec); ++it) {
        if (*it == target_value) {
            *it = new_value;
        }
    }
}

void print_int_vector(const std::vector<int>& vec)
{
    bool isFirst = true;

    for (auto e : vec) {
        if (!isFirst) {
            std::cout << ", ";
        }
        std::cout << e;
        isFirst = false;
    }
    std::cout << "\n";
}

実行結果:

1, 10, 10, 3, 10, 4

print_int_vector関数は、問題② で作ったものを流用しています。

値の置き換えを行う replace_int_vector_value関数の仮引数は、std::vector<int>型の値を受け取るための vec と、対象の値を受け取る target_value、置き換え後の値を受け取る new_value の3つです。関数内で値を変更しなければならないので、vec は参照型にしなければなりません。

仮引数vec は参照型ですから、vec は replace_int_vector_value関数を呼び出すときに実引数に指定した std::vector<int>型の変数の別名であり、同一体です。そのため、std::begin(vec)std::end(vec) によって得られるイテレータは、呼び出し元にある std::vector<int>型の変数から直接得られるものと同じです。したがって、*it = new_vector によって、呼び出し元にある std::vector<int> の要素の値が書き換わります。

問題5 (応用★★)

蔵書リストのプログラムには、本の情報を出力しているコードが何か所かに存在しています。この部分を1つの関数に共通化してください。

蔵書リストのプログラムのソースコードは、前のページの練習問題の解答ページにあります。


問題のコードは、以下の5行です。これが何度も登場しています。

std::cout << "書名: " << book.title << "\n"
          << "著者名: " << book.author << "\n"
          << "価格: " << book.price << "\n"
          << "評価: " << book.evaluate << "\n"
          << "\n";                  

これを関数にまとめます。book.title など、Book構造体のデータメンバにアクセスできる必要があるので、引数で Book型の値を受け取れるようにしましょう。構造体のサイズは大きいので、参照を使います(本編解説)。また、関数内では書き換えないので、const参照にします(本編解説)。

仮引数のところで Book が登場することになるため、Book構造体の定義を、ソースファイルの上の方に持ってくる必要もあります(本編解説)。

結果、ソースコード全体は次のようになります。

#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

struct Book {
    std::string    title;      // 書名
    std::string    author;     // 著者名
    int            price;      // 価格
    int            evaluate;   // 評価
};

void print_book_data(const Book& book)
{
    std::cout << "書名: " << book.title << "\n"
              << "著者名: " << book.author << "\n"
              << "価格: " << book.price << "\n"
              << "評価: " << book.evaluate << "\n"
              << "\n";
}

int main()
{
    constexpr auto booklist_path = "booklist.txt";

    std::vector<Book> books {};
    bool isChanged {false};

    while (true) {
        std::cout << "----------------------------\n"
                  << "コマンドを入力してください。\n"
                  << "a: 本を登録する。\n"
                  << "d: 本を削除する。\n"
                  << "c: すべての本を削除する。\n"
                  << "p: リストを確認する。\n"
                  << "st: 書名で検索する。\n"
                  << "sa: 著者名で検索する。\n"
                  << "sp: 価格で検索する。\n"
                  << "se: 評価で検索する。\n"
                  << "r: ファイルから読み込む。\n"
                  << "w: ファイルに書き込む。\n"
                  << "e: 終了する。\n"
                  << "----------------------------\n";

        std::string command {};
        std::getline(std::cin, command);
        if (command == "a") {
            // 本を登録する

            Book book {};
            std::istringstream iss {};
            std::string tmp {};

            std::cout << "書名を入力してください。\n";
            std::getline(std::cin, book.title);

            std::cout << "著者名を入力してください。\n";
            std::getline(std::cin, book.author);

            std::cout << "価格を入力してください。\n";
            while (true) {
                std::getline(std::cin, tmp);
                iss.str(tmp);
                iss >> book.price;
                if (!iss || book.price < 0) {
                    std::cout << "価格は 0以上の整数で入力してください。\n";
                    iss.clear();
                }
                else {
                    break;
                }
            }

            std::cout << "評価を入力してください。\n";
            std::getline(std::cin, tmp);
            iss.clear();
            iss.str(tmp);
            iss >> book.evaluate;
            if (!iss) {
                std::cout << "入力内容に不備があります。\n";
            }
            else {
                books.push_back(book);
                isChanged = true;
                std::cout << "登録しました。\n";
            }
        }
        else if (command == "d") {
            // 本を削除する

            std::cout << "書名を入力してください。\n";
            std::string title {};
            std::getline(std::cin, title);

            auto it = std::find_if(std::cbegin(books), std::cend(books), [title](Book e){ return e.title == title; });
            if (it == std::cend(books)) {
                std::cout << "指定された本は登録されていません。\n";
            }
            else {
                books.erase(it);
                isChanged = true;
                std::cout << "削除しました。\n";
            }
        }
        else if (command == "c") {
            // すべての本を削除する

            books.clear();
            isChanged = true;
            std::cout << "削除しました。\n";
        }
        else if (command == "p") {
            // リストを確認する

            for (auto& book : books) {
                print_book_data(book);
            }
        }
        else if (command == "st") {
            // 書名で検索する

            std::cout << "書名を入力してください。\n";
            std::string title {};
            std::getline(std::cin, title);

            for (auto& book : books) {
                if (book.title == title) {
                    print_book_data(book);
                }
            }
        }
        else if (command == "sa") {
            // 著者名で検索する

            std::cout << "著者名を入力してください。\n";
            std::string author {};
            std::getline(std::cin, author);

            for (auto& book : books) {
                if (book.author == author) {
                    print_book_data(book);
                }
            }
        }
        else if (command == "sp") {
            // 価格で検索する

            std::cout << "価格を入力してください。\n";
            std::string tmp {};
            std::getline(std::cin, tmp);
            std::istringstream iss {tmp};
            int price {};
            iss >> price;
            if (!iss || price < 0) {
                std::cout << "入力内容に不備があります。\n";
            }
            else {
                for (auto& book : books) {
                    if (book.price >= price) {
                        print_book_data(book);
                    }
                }
            }
        }
        else if (command == "se") {
            // 評価で検索する

            std::cout << "評価を入力してください。\n";
            std::string tmp {};
            std::getline(std::cin, tmp);
            std::istringstream iss {tmp};
            int evaluate {};
            iss >> evaluate;
            if (!iss) {
                std::cout << "入力内容に不備があります。\n";
            }
            else {
                for (auto& book : books) {
                    if (book.evaluate >= evaluate) {
                        print_book_data(book);
                    }
                }
            }
        }
        else if (command == "r") {
            // ファイルから読み込む

            std::ifstream ifs(booklist_path);
            if (!ifs) {
                std::cout << "ファイルが開けません。\n";
                continue;
            }

            // メモリ上の本のデータを消す
            books.clear();

            while (!ifs.eof()) {
                Book book {};
                std::istringstream iss {};
                std::string tmp {};

                std::getline(ifs, book.title);
                if (book.title.empty()) {
                    break;
                }

                std::getline(ifs, book.author);

                std::getline(ifs, tmp);
                iss.str(tmp);
                iss >> book.price;
                iss.clear();

                std::getline(ifs, tmp);
                iss.str(tmp);
                iss >> book.evaluate;

                books.push_back(book);
            }

            isChanged = false;
            std::cout << "読み込みました。\n";
        }
        else if (command == "w") {
            // ファイルに書き込む

            std::ofstream ofs(booklist_path);
            if (!ofs) {
                std::cout << "ファイルが開けません。\n";
                continue;
            }

            for (auto& book : books) {
                ofs << book.title << "\n"
                    << book.author << "\n"
                    << book.price << "\n"
                    << book.evaluate << "\n";
            }

            ofs.close();
            if (!ofs) {
                std::cout << "書き込みに失敗しました。\n";
                continue;
            }

            isChanged = false;
            std::cout << "書き込みました。\n";
        }
        else if (command == "e") {
            // 終了する

            if (isChanged) {
                std::cout << "変更した内容をファイルに書き込んでいませんが、終了してよろしいですか? (Y/N)\n";
                std::string s {};
                std::cin >> s;
                if (s != "Y" && s != "y") {
                    continue;
                }
            }

            std::cout << "終了します。\n";
            return 0;
        }
        else {
            // 無効なコマンド
            continue;
        }
    }
}


参考リンク



更新履歴




はてなブックマーク に保存 Pocket に保存 Facebook でシェア
Twitter でツイート Twitter をフォロー LINE で送る
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー