C++20 memperkenalkan Ranges Library — abstraksi di atas iterator yang memungkinkan komposisi algoritma secara deklaratif. Alih-alih menulis loop bertingkat atau beberapa pass dengan iterator, kita dapat menulis pipeline seperti v | filter | transform | take(3). Ranges bersifat lazy — elemen diproses saat diiterasi, sehingga komposisi beberapa view tidak menghasilkan container intermediate. Pada Chapter ini kita akan mendemonstrasikan komposisi dengan std::views dan std::ranges::* algorithm — disarankan mengkompilasi dengan g++ -std=c++20.

Dua Komponen Utama #

KomponenFungsi
std::ranges::*Versi algoritma STL yang menerima range (bukan pasangan iterator) — ranges::sort(v), ranges::find(v, x), ranges::min(v).
std::views::*Adaptor yang menghasilkan view — range lazily evaluated yang tidak menyalin data.

View adalah pair (underlying range + transformasi). Saat diiterasi, view meminta elemen dari underlying range satu per satu, menerapkan transformasi, lalu mengembalikan hasil.

Mekanisme: Lazy Evaluation #

Kode v | views::filter(p) | views::transform(f) | views::take(3) tidak memproses elemen apapun saat deklarasi. Hanya saat iterasi (for (auto x : hasil)), pipeline dimulai:

  1. take(3) meminta satu elemen dari transform.
  2. transform meminta satu elemen dari filter dan menerapkan f.
  3. filter meminta elemen dari v satu per satu sampai menemukan yang memenuhi predikat p.
  4. Proses berulang sampai take(3) terpenuhi.

Akibatnya: bila v berukuran sejuta elemen tetapi hanya tiga yang lolos filter, pipeline hanya memproses elemen yang diperlukan. Tidak ada alokasi container intermediate.

Kode: Pipeline dengan Views #

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

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    // 1) views::filter — hanya elemen genap
    auto genap = v | std::views::filter([](int x) { return x % 2 == 0; });
    std::cout << "filter genap    : ";
    for (int x : genap) std::cout << x << ' ';
    std::cout << '\n';

    // 2) views::transform — kalikan 10
    auto kali10 = v | std::views::transform([](int x) { return x * 10; });
    std::cout << "transform x10   : ";
    for (int x : kali10) std::cout << x << ' ';
    std::cout << '\n';

    // 3) Pipeline: filter genap -> transform x10 -> take 3
    auto hasil = v
        | std::views::filter([](int x) { return x % 2 == 0; })
        | std::views::transform([](int x) { return x * 10; })
        | std::views::take(3);
    std::cout << "pipeline        : ";
    for (int x : hasil) std::cout << x << ' ';
    std::cout << '\n';

    // 4) views::take / drop
    auto t3 = v | std::views::take(3);
    auto d3 = v | std::views::drop(7);
    std::cout << "take(3)         : ";
    for (int x : t3) std::cout << x << ' ';
    std::cout << '\n';
    std::cout << "drop(7)         : ";
    for (int x : d3) std::cout << x << ' ';
    std::cout << '\n';

    // 5) views::reverse
    std::cout << "reverse         : ";
    for (int x : v | std::views::reverse) std::cout << x << ' ';
    std::cout << '\n';

    // 6) views::iota — range bilangan
    std::cout << "iota(1,6)       : ";
    for (int x : std::views::iota(1, 6)) std::cout << x << ' ';
    std::cout << '\n';

    // 7) Lazy evaluation — take dari iota infinite
    long long sum = 0;
    for (int x : std::views::iota(1) | std::views::take(100)) sum += x;
    std::cout << "sum 1..100      = " << sum << '\n';

    // 8) ranges::sort, ranges::min, ranges::max
    std::vector<int> w = {5, 2, 8, 1, 9};
    std::ranges::sort(w);
    std::cout << "ranges::sort    : ";
    for (int x : w) std::cout << x << ' ';
    std::cout << "(min=" << std::ranges::min(w)
              << ", max=" << std::ranges::max(w) << ")\n";

    // 9) ranges::find_if
    auto it = std::ranges::find_if(v, [](int x) { return x > 5; });
    if (it != v.end()) std::cout << "first > 5       = " << *it << '\n';

    return 0;
}

Output yang Dihasilkan:

text
filter genap    : 2 4 6 8 10 
transform x10   : 10 20 30 40 50 60 70 80 90 100 
pipeline        : 20 40 60 
take(3)         : 1 2 3 
drop(7)         : 8 9 10 
reverse         : 10 9 8 7 6 5 4 3 2 1 
iota(1,6)       : 1 2 3 4 5 
sum 1..100      = 5050
ranges::sort    : 1 2 5 8 9 (min=1, max=9)
first > 5       = 6

Bedah Kode Baris-per-Baris #

  1. v | std::views::filter(...): operator | di-overload untuk komposisi view. filter menghasilkan view yang, saat diiterasi, meminta elemen dari v dan hanya meneruskan yang memenuhi predikat. Output {2,4,6,8,10}.
  2. v | std::views::transform(...): setiap elemen v dipetakan via lambda x * 10. Output {10,20,...,100}. Tidak ada alokasi container baru.
  3. Pipeline v | filter | transform | take(3): tiga view dikomposisikan. Pipeline memproses elemen secara lazy — saat take(3) sudah terisi tiga elemen (20, 40, 60), iterasi berhenti. Elemen {7,8,9,10} tidak pernah diproses.
  4. std::views::take(n): menghasilkan paling banyak n elemen pertama. Bila range lebih pendek, kembalikan semua.
  5. std::views::drop(n): melewatkan n elemen pertama, meneruskan sisanya. Sering dipakai untuk paging atau split train/test.
  6. std::views::reverse: view iterasi mundur. Container underlying harus mendukung bidirectional iterator.
  7. std::views::iota(1, 6): range [1, 6) — yaitu {1,2,3,4,5}. Pola umum mengganti loop for (int i = 1; i < 6; ++i).
  8. std::views::iota(1) (satu argumen): infinite range — tidak boleh diiterasi langsung. Dikombinasikan dengan take(100), ia menghasilkan {1..100}. Inilah demonstrasi lazy evaluation: range infinite tetap aman karena view membatasi paling banyak 100 elemen.
  9. std::ranges::sort(w): algoritma sort versi ranges. Tidak perlu menulis w.begin(), w.end() — cukup lewatkan container. Sama-sama O(n log n).
  10. std::ranges::min(w) / std::ranges::max(w): mengembalikan elemen minimum/maksimum. Menghilangkan kebutuhan std::min_element(w.begin(), w.end()) lalu dereference.
  11. std::ranges::find_if(v, pred): cari elemen pertama yang memenuhi predikat. Mengembalikan iterator (bukan view).

View yang Umum Dipakai #

ViewFungsi
views::filter(pred)Saring elemen yang memenuhi predikat
views::transform(f)Petakan setiap elemen via f
views::take(n)Ambil paling banyak n elemen pertama
views::drop(n)Lewati n elemen pertama
views::reverseIterasi mundur
views::iota(a, b)Range [a, b) bertambah satu
views::keys / views::valuesIterasi key/value dari map
views::single(x)Range berisi satu elemen
views::empty<T>Range kosong bertipe T

Ringkasan #

  • C++20 Ranges memungkinkan komposisi algoritma secara deklaratif dengan operator |.
  • std::views::* menghasilkan lazy view — tidak menyalin data; elemen diproses saat iterasi.
  • std::ranges::* adalah versi algoritma STL yang menerima range langsung (ranges::sort(v)).
  • Pipeline v | filter | transform | take hanya memproses elemen yang diperlukan — efisien untuk data besar.
  • Kompilasi dengan -std=c++20. Beberapa adaptor lanjutan butuh GCC 13+.
  • View umum: filter, transform, take, drop, reverse, iota, keys/values.