あなたは間違った仮定をしています:その型T
は と同じ型InputIterator
です。
しかしstd::accumulate
、一般的であり、さまざまな種類の創造的な蓄積と削減を可能にします。
例 #1: 従業員全体の給与を累積する
簡単な例を次に示しEmployee
ます。多くのデータ フィールドを持つクラスです。
class Employee {
/** All kinds of data: name, ID number, phone, email address... */
public:
int monthlyPay() const;
};
一連の従業員を有意義に「蓄積」することはできません。それは意味がありません。それは未定義です。ただし、従業員に関する累積を定義できます。すべての従業員の月給をすべて合計したいとしましょう。それを行うことができます:std::accumulate
/** Simple class defining how to add a single Employee's
* monthly pay to our existing tally */
auto accumulate_func = [](int accumulator, const Employee& emp) {
return accumulator + emp.monthlyPay();
};
// And here's how you call the actual calculation:
int TotalMonthlyPayrollCost(const vector<Employee>& V)
{
return std::accumulate(V.begin(), V.end(), 0, accumulate_func);
}
したがって、この例では、オブジェクトint
のコレクションに対して値を累積していEmployee
ます。ここで、累積合計は、実際に合計する変数の型とは異なります。
例 #2: 平均の累積
より複雑なタイプの累積にも使用できますaccumulate
-ベクトルに値を追加したい場合があります。入力全体で追跡している難解な統計があるかもしれません。など。あなたが蓄積するものは単なる数字である必要はありません。より複雑なものになる可能性があります。
たとえば、accumulate
int のベクトルの平均を計算するために を使用する簡単な例を次に示します。
// This time our accumulator isn't an int -- it's a structure that lets us
// accumulate an average.
struct average_accumulate_t
{
int sum;
size_t n;
double GetAverage() const { return ((double)sum)/n; }
};
// Here's HOW we add a value to the average:
auto func_accumulate_average =
[](average_accumulate_t accAverage, int value) {
return average_accumulate_t(
{accAverage.sum+value, // value is added to the total sum
accAverage.n+1}); // increment number of values seen
};
double CalculateAverage(const vector<int>& V)
{
average_accumulate_t res =
std::accumulate(V.begin(), V.end(), average_accumulate_t({0,0}), func_accumulate_average)
return res.GetAverage();
}
例 #3: 移動平均を累積する
初期値が必要なもう 1 つの理由は、その値が、作成している計算のデフォルト/ニュートラル値であるとは限らないためです。
これまで見てきた平均的な例に基づいて考えてみましょう。しかし今、移動平均を保持できるクラスが必要です。つまり、新しい値をフィードし続け、複数の呼び出しにわたってこれまでの平均を確認できます。
class RunningAverage
{
average_accumulate_t _avg;
public:
RunningAverage():_avg({0,0}){} // initialize to empty average
double AverageSoFar() const { return _avg.GetAverage(); }
void AddValues(const vector<int>& v)
{
_avg = std::accumulate(v.begin(), v.end(),
_avg, // NOT the default initial {0,0}!
func_accumulate_average);
}
};
int main()
{
RunningAverage r;
r.AddValues(vector<int>({1,1,1}));
std::cout << "Running Average: " << r.AverageSoFar() << std::endl; // 1.0
r.AddValues(vector<int>({-1,-1,-1}));
std::cout << "Running Average: " << r.AverageSoFar() << std::endl; // 0.0
}
std::accumulate
これは、その初期値を設定できることに絶対に依存しているケースです。さまざまな開始点から累積を初期化できる必要があります。
要約すると、入力範囲を反復処理し、その範囲全体で 1 つの結果を構築するstd::accumulate
場合に適しています。しかし、結果は範囲と同じ型である必要はなく、どの初期値を使用するかについて仮定を立てることはできません。これが、累積結果として使用する初期インスタンスが必要な理由です。