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 task

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.

Challenge 2

Write std::string initials(const std::vector<std::string>& names) that joins the first letter of every non-empty name, so {"natus", "vincere"} gives "nv". Use std::accumulate with a std::string starting value.

Challenge 3

Write std::vector<std::string> labels(const std::vector<int>& hp) that turns each hp value into "dead" (0 or less), "low" (1 to 30) or "ok" (above 30). Use std::transform.

Previous: find_if, count_if, any_of Next: Captures