C/C++ Arena

Step 4 of 6

accumulate and transform

Two more workhorses:

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

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

    int sum = std::accumulate(v.begin(), v.end(), 0);
    int product = std::accumulate(v.begin(), v.end(), 1, [](int acc, int x) { return acc * x; });
    double avg = std::accumulate(v.begin(), v.end(), 0.0) / v.size();
    std::cout << sum << " " << product << " " << avg << "\n";

    std::vector<int> cubes(v.size());
    std::transform(v.begin(), v.end(), cubes.begin(), [](int x) { return x * x * x; });
    for (int c : cubes) std::cout << c << " ";
    std::cout << "\n";

    std::string shout = "quiet please";
    std::transform(shout.begin(), shout.end(), shout.begin(), [](char c) { return (char)std::toupper(c); });
    std::cout << shout << "\n";
}
14 60 2.8
27 1 64 1 125 
QUIET PLEASE

The initial value decides the type

accumulate does its arithmetic in the type of the initial value. std::accumulate(v.begin(), v.end(), 0) adds ints and returns an int. For an average you want 0.0, so the sum is a double. Writing 0 there and then dividing is a classic bug: the sum is fine, but a range of doubles would be truncated to ints at every step.

transform needs room

std::transform writes to the output iterator you give it, but it doesn't create elements. That's why the example sizes cubes first with cubes(v.size()). The output can also be the input itself, as with shout, which changes the string in place.

Edge case: empty ranges

Averaging an empty vector divides by zero. Check v.empty() first and return the agreed default.

Your turn: write double average_damage(const std::vector<int>& hits) (0 for an empty vector) and std::vector<int> with_armor(const std::vector<int>& hits) that halves every hit (integer division). Use accumulate and transform.

Previous: find_if, count_if, any_of Next: Captures