Skip to content

🔄 Transform & Reduce — Functional Operations

transformaccumulateworkhorses của functional-style C++ — biến đổi và aggregate data một cách elegant.

std::transform — Map Operation

Single Range Transform

cpp
#include <algorithm>
#include <vector>
#include <iostream>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    std::vector<int> result(v.size());
    
    // Transform mỗi element
    std::transform(v.begin(), v.end(), result.begin(),
        [](int x) { return x * x; });
    // result = [1, 4, 9, 16, 25]
    
    // Transform in-place
    std::transform(v.begin(), v.end(), v.begin(),
        [](int x) { return x * 2; });
    // v = [2, 4, 6, 8, 10]
    
    return 0;
}

Two Ranges Transform

cpp
std::vector<int> a = {1, 2, 3, 4, 5};
std::vector<int> b = {10, 20, 30, 40, 50};
std::vector<int> c(a.size());

// Combine two ranges
std::transform(a.begin(), a.end(), b.begin(), c.begin(),
    [](int x, int y) { return x + y; });
// c = [11, 22, 33, 44, 55]

// Multiply pairwise
std::transform(a.begin(), a.end(), b.begin(), c.begin(),
    std::multiplies<int>());
// c = [10, 40, 90, 160, 250]

std::accumulate — Reduce/Fold

cpp
#include <numeric>
#include <vector>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    
    // Sum (default operation = +)
    int sum = std::accumulate(v.begin(), v.end(), 0);
    // 0 + 1 + 2 + 3 + 4 + 5 = 15
    
    // Product
    int product = std::accumulate(v.begin(), v.end(), 1,
        std::multiplies<int>());
    // 1 * 1 * 2 * 3 * 4 * 5 = 120
    
    // Custom accumulation
    int sumOfSquares = std::accumulate(v.begin(), v.end(), 0,
        [](int acc, int x) { return acc + x * x; });
    // 0 + 1 + 4 + 9 + 16 + 25 = 55
    
    return 0;
}

String Concatenation

cpp
std::vector<std::string> words = {"Hello", " ", "World", "!"};

std::string sentence = std::accumulate(
    words.begin(), words.end(), 
    std::string(),  // Initial value (empty string)
    [](const std::string& acc, const std::string& s) {
        return acc + s;
    });
// "Hello World!"

// Hoặc đơn giản hơn:
std::string s2 = std::accumulate(words.begin(), words.end(), std::string());

std::reduce (C++17) — Parallel-friendly

cpp
#include <numeric>
#include <execution>  // C++17 parallel

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

// Sequential reduce
int sum = std::reduce(v.begin(), v.end());

// Parallel reduce (C++17)
int psum = std::reduce(std::execution::par, v.begin(), v.end());

// Với initial value và binary op
int product = std::reduce(v.begin(), v.end(), 1, std::multiplies<int>());

📌 reduce vs accumulate

  • accumulate: Sequential, left-to-right, guaranteed order
  • reduce: May reorder operations, parallelizable
  • Dùng reduce khi operation là commutative và associative

std::inner_product — Dot Product

cpp
#include <numeric>

std::vector<int> a = {1, 2, 3};
std::vector<int> b = {4, 5, 6};

// Dot product: 1*4 + 2*5 + 3*6 = 32
int dot = std::inner_product(a.begin(), a.end(), b.begin(), 0);

// Generalized: init + sum(a[i] * b[i])
int result = std::inner_product(a.begin(), a.end(), b.begin(), 0,
    std::plus<>(),       // Reduce operation
    std::multiplies<>()  // Combine operation
);

std::partial_sum — Prefix Sums

cpp
#include <numeric>

std::vector<int> v = {1, 2, 3, 4, 5};
std::vector<int> prefix(v.size());

std::partial_sum(v.begin(), v.end(), prefix.begin());
// prefix = [1, 3, 6, 10, 15]
// 1, 1+2=3, 1+2+3=6, ...

// In-place
std::partial_sum(v.begin(), v.end(), v.begin());

std::adjacent_difference

cpp
std::vector<int> v = {1, 3, 6, 10, 15};
std::vector<int> diff(v.size());

std::adjacent_difference(v.begin(), v.end(), diff.begin());
// diff = [1, 2, 3, 4, 5]
// First stays same, rest = current - previous

std::iota — Generate Sequential Values

cpp
#include <numeric>

std::vector<int> v(10);
std::iota(v.begin(), v.end(), 1);
// v = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

std::iota(v.begin(), v.end(), 100);
// v = [100, 101, 102, ..., 109]

std::for_each

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

// Side effects (print)
std::for_each(v.begin(), v.end(), [](int x) {
    std::cout << x << " ";
});

// Modification
std::for_each(v.begin(), v.end(), [](int& x) {
    x *= 2;
});
// v = [2, 4, 6, 8, 10]

Chaining Operations

cpp
#include <algorithm>
#include <numeric>
#include <vector>

int main() {
    std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // Pipeline: filter evens → square → sum
    std::vector<int> evens;
    std::copy_if(data.begin(), data.end(), std::back_inserter(evens),
        [](int x) { return x % 2 == 0; });
    // evens = [2, 4, 6, 8, 10]
    
    std::transform(evens.begin(), evens.end(), evens.begin(),
        [](int x) { return x * x; });
    // evens = [4, 16, 36, 64, 100]
    
    int sum = std::accumulate(evens.begin(), evens.end(), 0);
    // 220
    
    return 0;
}

One-liner với Ranges (C++20)

cpp
// C++20 Ranges — elegant pipeline
#include <ranges>

auto result = data 
    | std::views::filter([](int x) { return x % 2 == 0; })
    | std::views::transform([](int x) { return x * x; });
    
int sum = std::accumulate(result.begin(), result.end(), 0);

Practical Examples

Calculate Statistics

cpp
std::vector<double> grades = {85, 90, 78, 92, 88, 95, 73, 89};

double sum = std::accumulate(grades.begin(), grades.end(), 0.0);
double avg = sum / grades.size();

auto [minIt, maxIt] = std::minmax_element(grades.begin(), grades.end());
double min = *minIt;
double max = *maxIt;

std::cout << "Average: " << avg << std::endl;  // 86.25
std::cout << "Min: " << min << ", Max: " << max << std::endl;  // 73, 95

Word Lengths

cpp
std::vector<std::string> words = {"hello", "world", "cpp", "programming"};
std::vector<size_t> lengths(words.size());

std::transform(words.begin(), words.end(), lengths.begin(),
    [](const std::string& s) { return s.length(); });
// lengths = [5, 5, 3, 11]

size_t totalChars = std::accumulate(lengths.begin(), lengths.end(), 0ULL);
// 24

📚 Tổng kết

AlgorithmPurposeExample
transformApply function to eachSquare all
accumulateReduce to single valueSum
reduce (C++17)Parallel-friendly accumulate
inner_productDot producta·b
partial_sumPrefix sumsRunning total
iotaGenerate sequence1, 2, 3, ...
for_eachApply side effectsPrint all

➡️ Tiếp theo

Tiếp theo: Code Golf — STL power demonstrations.