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

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

ただいまの
回答率

90.51%

  • C++

    3461questions

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

C++ 合計値 範囲指定

受付中

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 664

totake

score 2

C++で合計値を出力するプログラムを作成しています。プログラムを実行するとすべての和が出力されるのですが、
これを指定した範囲、例えば2、3、4、5の合計値を出力したい場合どうしたらいいのでしょうか?
よろしくお願いします。

#include <iostream>
#include <vector>
#include <string>
#include <numeric>

int main()
{
  const std::vector<int> v = {1, 2, 3, 4, 5};

  // 合計値を求める
  int sum = std::accumulate(v.begin(), v.end(), 0);
  std::cout << "sum : " << sum << std::endl;
}
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>

struct score {
    std::string name;
    int aaa;
    int bbb;
    int ccc;

    score(std::string n, int a, int b, int c) :
        name(n), aaa(a), bbb(b), ccc(c) {}
};


class score_a {
public:   
    bool operator()(const score& value) const {
        return value.aaa ;
    }
};

int main() {
    std::vector<score> v1;
    v1.push_back(score("青木", 5, 10, 15));

    int tokuten  = std::count_if(v1.begin(), v1.end(), score_a());
    std::cout << "青木:" << tokuten << std::endl;

    return 0;
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+1

こんにちは。

寝る前にちょっと遊んでみました。外れてそうな気もしますが、参考になるかも。
sum()関数が見慣れない形なのは、scoresを生配列にも対応させたからです。
(C++11が必要です。)

#include <string>
#include <vector>
#include <numeric>
#include <iostream>

struct Person
{
    std::string     name;
#if 0
    int             scores[5];
#else
    std::vector<int> scores;
#endif
};

template<class tContainer>
int sum(tContainer const& iContainer)
{
    return  std::accumulate(std::begin(iContainer), std::end(iContainer), 0);
}

int main()
{
    std::vector<Person> aPersons =
    {
        {"佐藤", { 1,  2,  3}},
        {"鈴木", {11, 12, 13, 14}},
        {"田中", {21, 22, 23, 24, 25}}
    };

    for(auto& person : aPersons)
    {
        std::cout << person.name << " : " << sum(person.scores) << " (";
        bool first = true;
        for(auto score : person.scores)
        {
            if (!first) std::cout << ", ";
            first = false;
            std::cout << score;
        }
        std::cout << ")\n";
    }
}


wandboxで結果をみる

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

もう少しだけ詳しい仕組みも説明しようかと。

 イテレータについて

イテレータは、簡単に言えば「ある要素の位置」を表すものです。簡単に言えばですが。std::vector もイテレータを実装していて、メンバ関数begin()によって要素の先頭を指すイテレータ、end()によって要素の末尾を指すイテレータを得ることができます。

C++のSTL関数では、範囲を受け取るときに範囲の「開始位置を指すイテレータ」と「終了位置を指すイテレータ」を取るものが多くあります。上のソースで使われている std::accumulate() もその一つです。

std::accumulate(開始位置, 終了位置, 初期値);


このように書くと、開始位置から終了位置までの要素の和 (を初期値に足したもの) を求めることができます。だから、

std::accumulate(v.begin(), v.end(), 0);


というのは「初期値を0としてv.begin()からv.end()までの要素を全部足していく」という意味になります。v.begin()は先頭要素を指すイテレータを返しますし、v.end()は末尾要素を指すイテレータを返しますので、結局「先頭から末尾までの要素の値の和」ということになります。

では、質問の例のように

const std::vector<int> v = {1, 2, 3, 4, 5};


の {2, 3, 4, 5} の和を求めるときにはどうすればよいか。

この範囲は、「先頭の次の要素」から「末尾の要素」まで、ということになりますね。なので、これらの位置を表すイテレータを得て、それを開始位置と終了位置に指定してやればよいわけです。あるイテレータをいくつか進めたイテレータを得るには std::next(イテレータ, 進める数) を使います。進める数は、指定しなければデフォルトで1になります。逆の働きをする std::prev(イテレータ, 戻す数) という関数もあります。これは末尾から戻す必要があるときに使えます。

以上のことから、次のように書けば求める結果が得られます。

int sum = std::accumulate (
    std::next(v.begin()), // 開始位置 --- 先頭から1進めたもの。
    v.end(),              // 終了位置 --- 末尾
    0                     // 初期値 --- 要素だけの和をとるなら0ですよね
);

ちなみに、終了位置は必ずしも末尾から考える必要もなく、和をとりたい範囲が「先頭の次の要素」から要素3つ分、つまり「[先頭の次の要素]の3つ先」まで (例のvなら {2, 3, 4}) だった場合は次のようにすればよいです。

// 開始位置 := 先頭の次の要素
auto beg = std::next(v.begin(), 1);

int sum = std::accumulate (
    beg,               // 開始位置 つまり 先頭の次の要素
    std::next(beg, 3), // 「開始位置」に指定したものの3つ先
    0                  // 初期値は0
);

std::vectorのイテレータは実は足し算ができるので、std::next()を使わずv.begin() + 1とかbeg + 3とかでもいくつか進めたイテレータを得ることができます。

いくらかごまかしている場所があって、そのうちの1つは、実は終了位置が指す要素は範囲に含まないということです。要するに [begin, end) なる半開区間ということです。そのために end() は実は末尾位置の"1つ先"を返しています。

 質問文後半のプログラムについて

std::count_if関数は、

std::count_if(開始位置, 終了位置, 条件の関数);


で、範囲内で 条件の関数が true を返す「要素の数」 を数える関数です。

int tokuten  = std::count_if(v1.begin(), v1.end(), score_a());


を翻訳すると


tokuten に「v1の先頭から末尾まで」の範囲で、関数 score_a()(要素) が true を返す要素の数を代入せよ。

つまり

tokuten に「v1の先頭から末尾まで」の範囲で、メンバ変数 aaa が true となる要素の数を代入せよ。

つまり (aaa は int なので)

tokuten に「v1の先頭から末尾まで」の範囲で、メンバ変数 aaa が 0 でない要素の数 を代入せよ。


今 v1 には score("青木", 5, 10, 15) という要素があります。この要素の aaa は 5 であり、5 は 0 でないので条件に一致します。他に要素がないので std::count_if() の仕事は終了です。
結果、条件に一致した v1 の要素の数は 1 つということでtokuten == 1となります。aaa の中にある 5 を出力したいのなら、素直に int tokuten = v1[0].aaa; とすればよいです。


3つの得点の和がほしい場合でも LouiS0616 さんもおっしゃっている通り愚直に aaa + bbb + ccc とするしかないです。

便利な関数群が使えるように、少し改変して配列を使うようにしてみました。例えば次のようになります。ついでに青木くん以外のクラスメイトも増やしてみました。

#include <string>
#include <vector>
#include <numeric>
#include <algorithm>
#include <iostream>
#include <array>

constexpr const size_t NUMOF_SUBJECTS = 3;
// std::array を使いますが、ちょっと長くて面倒なので別名をつけます。
using score_array = std::array<int, NUMOF_SUBJECTS>;

struct score {
    std::string name;
    score_array scores;

    score(std::string n, score_array const &s)
        : name(n), scores(s) {}

    int get_total_score() {
        return std::accumulate(scores.begin(), scores.end(), 0);
    }
};

int main() {
    std::vector<score> v1;
    v1.push_back(score(
        "青木",
        score_array{{5, 10, 15}}
    ));
    v1.push_back(score(
        "田中",
        score_array{{3, 20, 4}}
    ));
    v1.push_back(score(
        "出来内",
        score_array{{0, 0, -3}}
    ));

    // range-based for というのを使うともう少し楽に書けますが。
    // C言語的にループしましょう。
    for (size_t i = 0; i < v1.size(); i++) {
        std::cout
            << v1[i].name
            << ":"
            << v1[i].get_total_score()
            << std::endl;
    }

    return 0;
}


実行結果:

青木:30
田中:27
出来内:-3

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

std::advanceを使うと良いかと思います。

#include <cassert>
#include <iterator>
#include <iostream>
#include <numeric>
#include <vector>

int slice_sum(const std::vector<int>& v, size_t from, size_t to) {
    assert(to <= v.size());

    auto it_begin = v.cbegin(), it_end = v.cend();
    std::advance(it_begin, from);
    std::advance(it_end, to-v.size());

    return std::accumulate(it_begin, it_end, 0);
}

int main(void) {
    const std::vector<int> v = {1, 2, 3, 4, 5};

    int sum = slice_sum(v, 1, v.size());
    std::cout << "sum : " << sum << std::endl;

    return 0;
}

修正:yumetodoさんからご指摘を受けて

今回の場合、std::nextの方が簡潔に処理を記述できるようです。

yumetodoさんが書かれたslice_sum関数はこちら。

template<typename T>
int slice_sum(const std::vector<T>& v, size_t from, size_t to) {
    assert(to <= v.size());
    return std::accumulate(std::next(v.cbegin(), from), std::next(v.cbegin(), to), T{});
}

コメントを受けて

構造体を利用する例。

#include <iostream>
#include <numeric>
#include <vector>

struct Person {
    Person(const std::string& name, const std::vector<int>& scores)
        : name_(name), scores_(scores) { }

    auto& get_name(void) const noexcept {
        return this->name_;
    }
    auto& get_scores(void) const noexcept {
        return this->scores_;
    }

private:
    const std::string& name_;
    const std::vector<int> scores_;
};

int main(void) {
    Person p("青木", {2, 3, 4, 5});

    int sum = std::accumulate(
        p.get_scores().cbegin(),
        p.get_scores().cend(),
        0
    );
    std::cout << p.get_name() << " sum : " << sum << std::endl;

    return 0;
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/24 00:58

    ご回答ありがとうございます。
    v = {1, 2, 3, 4, 5}の部分なんですけど、1の部分を日本語の名前例えば、
    v = {"青木", 2, 3, 4, 5}などにしたい場合はどうすればいいでしょうか?
    そもそも日本語名を使うことはできますか?

    キャンセル

  • 2018/01/24 01:02 編集

    基本的に型の違うデータを同じvectorに押し込むことは出来ないですね。
    適切に構造体/クラスを組むか、連想配列を使えばいいんじゃないでしょうか。

    キャンセル

  • 2018/01/24 01:04

    それは何がしたいかによります。もうすこしやりたいことと目的を詳しく補足してください。現状だとstd::variantをすすめるべきなのかもっと適切な方法をすすめるべきか判別つきません

    キャンセル

  • 2018/01/24 01:08

    ご指摘ありがとうございます。
    今やりたいことは、v = {"青木", 2, 3, 4, 5}と想定して、
    名前:合計値 と出力させたいのですが、
    青木:14 のように出力させたいです。

    キャンセル

  • 2018/01/24 01:19

    それは
    std::pair<std::string, std::vector<int>> v = { "青木", { 2, 3, 4, 5}};
    のようにしてはだめですかね・・・?
    https://wandbox.org/permlink/7bZjQRblLSSiUOcm

    キャンセル

  • 2018/01/24 01:20

    std::vector<int>の要素型はintであり、文字列は格納できないんですね。でまた、一つの配列にいろんなデータが有るというのは根本的に間違っているので、上記提案をしました。

    キャンセル

  • 2018/01/24 01:21

    >Personクラス
    そちらのほうが意味的にはわかりやすいですが。

    キャンセル

  • 2018/01/24 01:26

    今後どれくらい属性が増えるか分からないですものね。
    yumetodoさんのように色んなアイデアが出てこないもので、非常に参考になります。

    キャンセル

  • 2018/01/24 01:55

    それはそうと、std::advanceは引数を書き換えるのでstd::nextのほうが一般に扱いやすいと思われます。

    キャンセル

  • 2018/01/24 02:38

    もう1つソースコードを載せさせていただきました。
    今のままでは、1と出力されるのですが、
    これをaaaの場所にある5という数字を出力することはできますでしょうか?
    またこれから5,10,15を足して出力することはできますでしょうか?

    キャンセル

  • 2018/01/24 14:42

    @yumetodo さん
    回答に引用する形で紹介させていただきました。ご指摘ありがとうございます。

    キャンセル

  • 2018/01/24 14:48

    @totake さん
    > 今のままでは、1と出力されるのですが、これをaaaの場所にある5という数字を出力することはできますでしょうか?
    ---
    もともと数えるためにcount_ifに放り込んだのですよね?

    > またこれから5,10,15を足して出力することはできますでしょうか?
    ---
    コンテナではなく別々の変数に値が格納されているので、愚直に足すしかないと思います。

    キャンセル

  • 2018/01/24 18:42

    >愚直に足すしかない
    一応initizer_listにぶち込むという手法もないわけではないですが(イテレータが使える)

    キャンセル

  • 2018/01/24 18:58

    std::array<int, 3> arr{aaa, bbb, ccc}; ってことですか?

    キャンセル

  • 2018/01/24 19:31

    それstd::arrayや。まあそれでもいいけど

    キャンセル

  • 2018/01/24 20:07

    auto list = {aaa, bbb, ccc};
    ということですよね。まあ結局列挙すると考えると、この例では素直に+演算子を書くほうが楽ではありますが。

    キャンセル

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

  • ただいまの回答率 90.51%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C++

    3461questions

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