C++で合計値を出力するプログラムを作成しています。プログラムを実行するとすべての和が出力されるのですが、
これを指定した範囲、例えば2、3、4、5の合計値を出力したい場合どうしたらいいのでしょうか?
よろしくお願いします。
C++
1#include <iostream> 2#include <vector> 3#include <string> 4#include <numeric> 5 6int main() 7{ 8 const std::vector<int> v = {1, 2, 3, 4, 5}; 9 10 // 合計値を求める 11 int sum = std::accumulate(v.begin(), v.end(), 0); 12 std::cout << "sum : " << sum << std::endl; 13}
C++
1#include <string> 2#include <vector> 3#include <algorithm> 4#include <iostream> 5 6struct score { 7 std::string name; 8 int aaa; 9 int bbb; 10 int ccc; 11 12 score(std::string n, int a, int b, int c) : 13 name(n), aaa(a), bbb(b), ccc(c) {} 14}; 15 16 17class score_a { 18public: 19 bool operator()(const score& value) const { 20 return value.aaa ; 21 } 22}; 23 24int main() { 25 std::vector<score> v1; 26 v1.push_back(score("青木", 5, 10, 15)); 27 28 int tokuten = std::count_if(v1.begin(), v1.end(), score_a()); 29 std::cout << "青木:" << tokuten << std::endl; 30 31 return 0; 32}
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答3件
0
もう少しだけ詳しい仕組みも説明しようかと。
イテレータについて
イテレータは、簡単に言えば「ある要素の位置」を表すものです。簡単に言えばですが。std::vector
もイテレータを実装していて、メンバ関数begin()
によって要素の先頭を指すイテレータ、end()
によって要素の末尾を指すイテレータを得ることができます。
C++のSTL関数では、範囲を受け取るときに範囲の「開始位置を指すイテレータ」と「終了位置を指すイテレータ」を取るものが多くあります。上のソースで使われている std::accumulate()
もその一つです。
cpp
1std::accumulate(開始位置, 終了位置, 初期値);
このように書くと、開始位置から終了位置までの要素の和 (を初期値に足したもの) を求めることができます。だから、
cpp
1std::accumulate(v.begin(), v.end(), 0);
というのは「初期値を0としてv.begin()
からv.end()
までの要素を全部足していく」という意味になります。v.begin()
は先頭要素を指すイテレータを返しますし、v.end()
は末尾要素を指すイテレータを返しますので、結局「先頭から末尾までの要素の値の和」ということになります。
では、質問の例のように
cpp
1const std::vector<int> v = {1, 2, 3, 4, 5};
の {2, 3, 4, 5} の和を求めるときにはどうすればよいか。
この範囲は、「先頭の次の要素」から「末尾の要素」まで、ということになりますね。なので、これらの位置を表すイテレータを得て、それを開始位置と終了位置に指定してやればよいわけです。あるイテレータをいくつか進めたイテレータを得るには std::next(イテレータ, 進める数)
を使います。進める数は、指定しなければデフォルトで1になります。逆の働きをする std::prev(イテレータ, 戻す数)
という関数もあります。これは末尾から戻す必要があるときに使えます。
以上のことから、次のように書けば求める結果が得られます。
cpp
1int sum = std::accumulate ( 2 std::next(v.begin()), // 開始位置 --- 先頭から1進めたもの。 3 v.end(), // 終了位置 --- 末尾 4 0 // 初期値 --- 要素だけの和をとるなら0ですよね 5);
ちなみに、終了位置は必ずしも末尾から考える必要もなく、和をとりたい範囲が「先頭の次の要素」から要素3つ分、つまり「[先頭の次の要素]の3つ先」まで (例のv
なら {2, 3, 4}) だった場合は次のようにすればよいです。
cpp
1// 開始位置 := 先頭の次の要素 2auto beg = std::next(v.begin(), 1); 3 4int sum = std::accumulate ( 5 beg, // 開始位置 つまり 先頭の次の要素 6 std::next(beg, 3), // 「開始位置」に指定したものの3つ先 7 0 // 初期値は0 8);
std::vector
のイテレータは実は足し算ができるので、std::next()
を使わずv.begin() + 1
とかbeg + 3
とかでもいくつか進めたイテレータを得ることができます。
いくらかごまかしている場所があって、そのうちの1つは、実は終了位置が指す要素は範囲に含まないということです。要するに [begin, end) なる半開区間ということです。そのために end() は実は末尾位置の"1つ先"を返しています。
質問文後半のプログラムについて
std::count_if
関数は、
cpp
1std::count_if(開始位置, 終了位置, 条件の関数);
で、範囲内で 条件の関数が true を返す「要素の数」 を数える関数です。
cpp
1int 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
とするしかないです。
便利な関数群が使えるように、少し改変して配列を使うようにしてみました。例えば次のようになります。ついでに青木くん以外のクラスメイトも増やしてみました。
cpp
1#include <string> 2#include <vector> 3#include <numeric> 4#include <algorithm> 5#include <iostream> 6#include <array> 7 8constexpr const size_t NUMOF_SUBJECTS = 3; 9// std::array を使いますが、ちょっと長くて面倒なので別名をつけます。 10using score_array = std::array<int, NUMOF_SUBJECTS>; 11 12struct score { 13 std::string name; 14 score_array scores; 15 16 score(std::string n, score_array const &s) 17 : name(n), scores(s) {} 18 19 int get_total_score() { 20 return std::accumulate(scores.begin(), scores.end(), 0); 21 } 22}; 23 24int main() { 25 std::vector<score> v1; 26 v1.push_back(score( 27 "青木", 28 score_array{{5, 10, 15}} 29 )); 30 v1.push_back(score( 31 "田中", 32 score_array{{3, 20, 4}} 33 )); 34 v1.push_back(score( 35 "出来内", 36 score_array{{0, 0, -3}} 37 )); 38 39 // range-based for というのを使うともう少し楽に書けますが。 40 // C言語的にループしましょう。 41 for (size_t i = 0; i < v1.size(); i++) { 42 std::cout 43 << v1[i].name 44 << ":" 45 << v1[i].get_total_score() 46 << std::endl; 47 } 48 49 return 0; 50}
実行結果:
青木:30 田中:27 出来内:-3
投稿2018/01/24 06:57
総合スコア429
0
こんにちは。
寝る前にちょっと遊んでみました。外れてそうな気もしますが、参考になるかも。
sum()関数が見慣れない形なのは、scoresを生配列にも対応させたからです。
(C++11が必要です。)
C++
1#include <string> 2#include <vector> 3#include <numeric> 4#include <iostream> 5 6struct Person 7{ 8 std::string name; 9#if 0 10 int scores[5]; 11#else 12 std::vector<int> scores; 13#endif 14}; 15 16template<class tContainer> 17int sum(tContainer const& iContainer) 18{ 19 return std::accumulate(std::begin(iContainer), std::end(iContainer), 0); 20} 21 22int main() 23{ 24 std::vector<Person> aPersons = 25 { 26 {"佐藤", { 1, 2, 3}}, 27 {"鈴木", {11, 12, 13, 14}}, 28 {"田中", {21, 22, 23, 24, 25}} 29 }; 30 31 for(auto& person : aPersons) 32 { 33 std::cout << person.name << " : " << sum(person.scores) << " ("; 34 bool first = true; 35 for(auto score : person.scores) 36 { 37 if (!first) std::cout << ", "; 38 first = false; 39 std::cout << score; 40 } 41 std::cout << ")\n"; 42 } 43}
投稿2018/01/23 18:34
総合スコア23274
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
std::advanceを使うと良いかと思います。
C++
1#include <cassert> 2#include <iterator> 3#include <iostream> 4#include <numeric> 5#include <vector> 6 7int slice_sum(const std::vector<int>& v, size_t from, size_t to) { 8 assert(to <= v.size()); 9 10 auto it_begin = v.cbegin(), it_end = v.cend(); 11 std::advance(it_begin, from); 12 std::advance(it_end, to-v.size()); 13 14 return std::accumulate(it_begin, it_end, 0); 15} 16 17int main(void) { 18 const std::vector<int> v = {1, 2, 3, 4, 5}; 19 20 int sum = slice_sum(v, 1, v.size()); 21 std::cout << "sum : " << sum << std::endl; 22 23 return 0; 24}
修正:yumetodoさんからご指摘を受けて
今回の場合、std::nextの方が簡潔に処理を記述できるようです。
yumetodoさんが書かれたslice_sum関数はこちら。
C++
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{});
}
コメントを受けて
構造体を利用する例。
C++
1#include <iostream> 2#include <numeric> 3#include <vector> 4 5struct Person { 6 Person(const std::string& name, const std::vector<int>& scores) 7 : name_(name), scores_(scores) { } 8 9 auto& get_name(void) const noexcept { 10 return this->name_; 11 } 12 auto& get_scores(void) const noexcept { 13 return this->scores_; 14 } 15 16private: 17 const std::string& name_; 18 const std::vector<int> scores_; 19}; 20 21int main(void) { 22 Person p("青木", {2, 3, 4, 5}); 23 24 int sum = std::accumulate( 25 p.get_scores().cbegin(), 26 p.get_scores().cend(), 27 0 28 ); 29 std::cout << p.get_name() << " sum : " << sum << std::endl; 30 31 return 0; 32}
投稿2018/01/23 15:39
編集2018/01/24 05:42総合スコア35676
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/01/23 16:04
2018/01/23 16:19
2018/01/23 16:20
2018/01/23 16:21
2018/01/23 16:55
2018/01/24 09:42
2018/01/24 10:31
2018/01/24 11:07

あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。