Banyak pekerjaan data berbentuk pola yang sama: terapkan satu pengolahan ke setiap elemen. Potong pajak dari setiap harga, hitung panjang setiap kata, jumlahkan pasangan nilai dari dua rentang, atau kapitalkan setiap huruf. Menulis loop manual bisa saja dilakukan, tetapi std::transform menyatakannya dalam satu panggilan algoritma dengan maksud yang langsung terbaca. Algoritma ini punya dua bentuk: unary (satu rentang masuk, satu rentang keluar) dan binary (dua rentang masuk, satu rentang keluar). Chapter ini membedah keduanya, ditambah pola in-place yang memakai rentang yang sama untuk input dan output.
transform Unary #
Bentuk unary: std::transform(first, last, d_first, f). Setiap elemen x pada [first, last) diproses dengan fungsi f(x), dan hasilnya ditulis ke posisi tujuan mulai d_first. Sama seperti std::copy, tujuan tidak dibesarkan otomatis — pakai back_inserter atau sediakan ukuran cukup. Berikut contoh lengkap yang menguji ketiga pola transform:
#include <algorithm>
#include <cctype>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
void cetak(const std::vector<int>& v) {
for (std::size_t i = 0; i < v.size(); ++i) {
if (i > 0) std::cout << ' ';
std::cout << v[i];
}
std::cout << '\n';
}
int main() {
std::vector<int> harga = {1000, 2500, 4000};
// Unary: satu rentang masuk, hasil ditulis ke rentang keluar baru
std::vector<int> diskon;
std::transform(harga.begin(), harga.end(), std::back_inserter(diskon),
[](int x) { return x - 200; });
std::cout << "Unary : ";
cetak(diskon);
// In-place: input dan output adalah rentang yang sama
std::transform(harga.begin(), harga.end(), harga.begin(),
[](int x) { return x * 2; });
std::cout << "In-place: ";
cetak(harga);
// Binary: dua rentang masuk, satu rentang keluar
std::vector<int> a = {1, 2, 3, 4};
std::vector<int> b = {10, 20, 30, 40};
std::vector<int> jumlah;
std::transform(a.begin(), a.end(), b.begin(), std::back_inserter(jumlah),
[](int x, int y) { return x + y; });
std::cout << "Binary : ";
cetak(jumlah);
// transform per karakter pada std::string
std::string teks = "cpp";
std::transform(teks.begin(), teks.end(), teks.begin(),
[](unsigned char c) { return std::toupper(c); });
std::cout << "String : " << teks << "\n";
return 0;
}Output yang Dihasilkan:
Unary : 800 2300 3800
In-place: 2000 5000 8000
Binary : 11 22 33 44
String : CPPBedah baris demi baris:
- Baris 17-19 (unary ke container baru): lambda
x - 200dipanggil untuk setiap harga, dan hasilnya{800, 2300, 3800}di-push_backlewatback_inserterke vectordiskon. Vector asli tidak berubah. - Baris 22-23 (in-place): di sini
d_firstadalahharga.begin()juga — input dan output memakai rentang yang sama. Setiap elemen ditimpa dengan hasil pengolahan dirinya, sehingga harga berubah menjadi dua kali lipat. Pola ini sah karena penulisan dilakukan urut dari depan. - Baris 27-31 (binary): bentuk binary menerima
first1, last1rentang pertama,first2awal rentang kedua, tujuan, lalu fungsi dua argumen. Lambdax + ymenjumlahkan pasangan elemen:1+10,2+20, dan seterusnya menjadi{11, 22, 33, 44}. - Baris 34-36 (per karakter): transform bekerja di container karakter apa pun. Lambda menerima
unsigned char— bukanchar— lalu memanggilstd::toupper. Konversi keunsigned charadalah rekomendasi standar untuk menghindari perilaku tidak terdefinisi bilacharbertanda dan nilainya negatif.
Dua catatan untuk versi binary. Rentang kedua hanya diberi titik awal first2, bukan [first2, last2) — jumlah elemennya mengikuti rentang pertama, jadi kita harus memastikan sendiri rentang kedua cukup panjang. Dan bila rentang kedua lebih pendek dari rentang pertama, pembacaan di luar batas adalah undefined behavior.
In-place atau Container Baru #
Pilihan pola tergantung kebutuhan. In-place (d_first == first) hemat memori dan cocok saat data lama memang tidak diperlukan lagi. Menulis ke container baru lewat back_inserter menjaga data asal — berguna saat kita butuh membandingkan sebelum dan sesudah, atau saat hasilnya akan menjadi data berbeda secara makna (misalnya harga dan harga-setelah-diskon adalah dua kolom berbeda).
Sejak C++20 tersedia pula std::ranges::transform yang menerima container langsung dan mendukung projection, sehingga mengubah field tertentu dari struct dapat ditulis tanpa lambda tambahan. Untuk sekarang cukup diingat sebagai versi ringkasnya; bahasan ranges tuntas di chapter penutup Section ini.
Transform mengubah isi elemen tanpa mengubah jumlahnya. Pertanyaan berikutnya justru sebaliknya: bagaimana cara mengurangi elemen dari vector — dan mengapa idiomnya bernama remove-erase — yang dibahas di chapter berikutnya.
Rujukan resmi: std::transform .