Mengubah sekian banyak elemen menjadi satu nilai — total penjualan, hasil perkalian faktor, rangkaian teks — adalah bentuk paling klasik dari agregasi. Header <numeric> menyediakan dua algoritma untuk itu. std::accumulate menjalankan fold kiri: mulai dari nilai awal, proses elemen satu per satu dari depan ke belakang, deterministik penuh. std::reduce mengerjakan pekerjaan yang sama untuk operasi penjumlahan sederhana, tetapi standar memberinya izin menggabungkan elemen dalam urutan bebas sehingga siap untuk eksekusi paralel. Perbedaan izin itulah yang menentukan kapan kita boleh memakai yang mana. Chapter ini membedah keduanya, peran nilai awal, dan cara memasang operasi khusus.

std::accumulate: Fold Kiri yang Deterministik #

std::accumulate(first, last, init) bekerja persis seperti ini: hasil = init, lalu untuk setiap elemen dari kiri ke kanan, hasil = hasil + elemen. Nilai awal init bukan formalitas — ia tipe dan titik mulai hasil. init berupa 0 menghasilkan total biasa; init berupa saldo awal 1000 langsung termasuk dalam hasil; dan tipe init menentukan tipe aritmetikanya (mengumpulkan double memerlukan init berupa 0.0, bukan 0, supaya tidak terpotong jadi bilangan bulat).

cpp
#include <algorithm>
#include <functional>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>

int main() {
    std::vector<int> penjualan = {120, 85, 300, 45};

    // accumulate: jumlahkan dari kiri dengan nilai awal 0
    int total = std::accumulate(penjualan.begin(), penjualan.end(), 0);
    std::cout << "Total (init 0)    : " << total << "\n";

    // Nilai awal bukan nol: saldo awal ikut dihitung
    int denganSaldo = std::accumulate(penjualan.begin(), penjualan.end(), 1000);
    std::cout << "Total (init 1000) : " << denganSaldo << "\n";

    // Operasi khusus: perkalian (karena default accumulate adalah penjumlahan)
    std::vector<int> faktor = {2, 3, 4};
    int hasilKali = std::accumulate(faktor.begin(), faktor.end(), 1,
                                    std::multiplies<int>{});
    std::cout << "Produk 2*3*4      : " << hasilKali << "\n";

    // accumulate juga bisa merangkai string
    std::vector<std::string> kata = {"iter", "ator", "algoritma"};
    std::string gabung =
        std::accumulate(kata.begin(), kata.end(), std::string());
    std::cout << "Gabungan          : " << gabung << "\n";

    // reduce: hasil sama, tapi standar mengizinkan pengurutan bebas
    int totalReduce = std::reduce(penjualan.begin(), penjualan.end(), 0);
    std::cout << "reduce (init 0)   : " << totalReduce << "\n";
    return 0;
}

Output yang Dihasilkan:

text
Total (init 0)    : 550
Total (init 1000) : 1550
Produk 2*3*4      : 24
Gabungan          : iteratoralgoritma
reduce (init 0)   : 550

Bedah baris demi baris:

  1. Baris 9-10 (init 0): penjumlahan biasa dari 120 + 85 + 300 + 45, hasil 550.
  2. Baris 13-14 (init 1000): nilai awal ikut dijumlahkan, hasil 1550. Pola ini berguna untuk menumpuk data di atas nilai yang sudah ada.
  3. Baris 18-20 (std::multiplies<int>{}): argumen keempat menggantikan operasi + default. Dengan init 1 dan operasi perkalian, hasilnya 2 * 3 * 4 = 24. std::multiplies adalah function object bawaan yang dibahas lebih lanjut di Chapter Function Objects.
  4. Baris 23-25 (merangkai string): accumulate dengan init berupa std::string() kosong menggabungkan seluruh elemen menjadi satu teks: iteratoralgoritma.
  5. Baris 28-29 (std::reduce): untuk penjumlahan int sederhana, hasilnya sama persis: 550.

Baris 23-25 layak dihormati sekaligus diwaspadai. Karena accumulate menambah string dari kiri satu per satu dan setiap penambahan menyalin isi string hasil sejauh ini, kompleksitasnya bisa menjadi kuadratik untuk data besar. Untuk merangkai banyak teks, pola yang lebih baik adalah menghitung total panjang dulu, membuat satu std::string berukuran cukup, lalu mengisi dengan copy — atau cukup untuk pembelajaran ini, tetap gunakan accumulate pada data kecil.

std::reduce: Ramah Paralel, dengan Syarat #

std::reduce(first, last, init) tanpa operasi khusus berperilaku seperti accumulate dengan penjumlahan — hasilnya identik pada program kita. Bedanya ada pada izin yang diberikan standar: reduce boleh menggabungkan elemen dalam urutan apa pun, termasuk mengelompokkan data lalu menjumlahkan tiap kelompok secara bersamaan. Izin inilah yang membuatnya cocok untuk eksekusi paralel, misalnya dengan kebijakan eksekusi (execution policy) yang dibahas di Section Concurrency dan Parallelism.

Konsekuensinya, ada syarat yang tidak diminta accumulate: operasinya harus asosiatif dan komutatif agar hasilnya tetap benar walau urutannya bebas. Penjumlahan dan perkalian bilangan memenuhi syarat itu; penggabungan string tidak asosiatif-murni dalam praktik (urutan menyusunnya mengubah karakteristik biaya), dan operasi yang bergantung pada urutan — misalnya menghitung (a - b) - c — jelas tidak boleh dipasangkan dengan reduce.

Keduanya juga punya pasangan accumulate-tanpa-init (memakai elemen pertama sebagai titik mulai) — namun bentuk dengan init eksplisit lebih aman karena langsung menangani rentang kosong tanpa risiko mendereference end().

Dari agregasi generik, chapter berikutnya melihat kotak perkakas <numeric> yang lebih lengkap: iota, inner_product, partial_sum, dan adjacent_difference.

Rujukan resmi: std::accumulate dan std::reduce .