Tiga fungsi meliputi kebutuhan pemrosesan urutan secara fungsional, yaitu map untuk mengubah setiap elemen, filter untuk menyaring elemen yang memenuhi predikat, dan reduce untuk melipat seluruh urutan menjadi satu nilai. Ketiganya bekerja atas iterator sebagaimana protokol iterasi, dan seluruhnya dapat ditulis ulang sebagai comprehension atau perulangan biasa. Topik ini membahas perilaku ketiganya, lalu membandingkannya secara jujur dengan comprehension supaya pilihannya dapat dipertanggungjawabkan.
map menerapkan fungsi pada setiap elemen#
Fungsi map(fungsi, iterable) memanggil fungsi yang diberikan untuk setiap elemen dan menghasilkan nilai kembaliannya dalam bentuk iterator. Hasilnya bukan list, sehingga pencetakan langsung tidak menampilkan isinya, dan konversi lewat list() diperlukan ketika seluruh hasil ingin dilihat sekaligus. Argumen pertamanya boleh berupa fungsi buatan sendiri maupun fungsi bawaan yang sudah tersedia.
def kuadrat(n):
return n * n
angka = [1, 2, 3, 4]
hasil = map(kuadrat, angka)
print(type(hasil).__name__)
print(list(hasil))
kata = ["satu", "dua", "tiga"]
print(list(map(str.upper, kata)))map
[1, 4, 9, 16]
['SATU', 'DUA', 'TIGA']Cetakan map menegaskan bahwa hasilnya berupa objek iterator, dan list() pada baris berikutnya menghabiskannya menjadi daftar utuh. Contoh kedua menunjukkan keunggulan map, yaitu meneruskan fungsi yang sudah ada seperti str.upper tanpa perlu menulis lambda sama sekali. Apabila fungsinya perlu ditulis terlebih dahulu hanya untuk pemanggilan ini, keunggulan itu mulai memudar, dan pembandingannya ada pada bagian akhir topik.
Fungsi ini menerima lebih dari satu iterable bila fungsinya menuntut argumen sebanyak itu, misalnya map(lambda a, b: a + b, nilai_a, nilai_b) menjumlahkan dua daftar sejajar elemen demi elemen. Sifat berhenti pada urutan terpendek berlaku di sini sebagaimana perilaku zip. Karena hasilnya iterator sekali pakai, konversi ke list diperlukan setiap kali datanya perlu dibaca kembali.
filter menyaring dengan predikat#
Fungsi filter(predikat, iterable) menghasilkan elemen yang lulus uji, yaitu elemen yang pemanggilan predikatnya mengembalikan nilai benar. Predikat berupa fungsi yang mengembalikan nilai benar atau salah, sehingga bentuk lambda yang pendek sering dipakai di posisi ini. Hasilnya juga berupa iterator yang bersifat lazy, dan elemen yang tidak lulus uji tidak memunculkan None maupun tanda apa pun.
nilai = [78, 92, 65, 88, 55]
lulus = filter(lambda n: n >= 70, nilai)
print(list(lulus))[78, 92, 88]Hanya nilai 78, 92, dan 88 yang lolos karena memenuhi syarat minimal 70, sedangkan sisanya ditinggalkan. Predikat tidak harus berupa lambda, dan fungsi bawaan pun sah dipakai pada posisi itu, misalnya str.islower atas deretan string. Bentuk khusus filter(None, data) mengembalikan elemen yang bernilai benar saja tanpa fungsi apa pun, meskipun predikat yang eksplisit biasanya lebih mudah dibaca. Ketika map dan filter dirangkai, data bergerak satu elemen demi satu elemen, yaitu elemen disaring lalu diubah, tanpa list perantara yang menampung hasil setiap tahap.
reduce melipat urutan menjadi satu nilai#
Fungsi reduce(fungsi, iterable, awal) dari modul functools mengerjakan urutan dari kiri ke kanan dengan satu akumulasi, yaitu hasil sementara yang diperbarui setiap langkah. Pada langkah pertama, akumulasi diisi nilai awal atau elemen pertama bila awal tidak diberikan, lalu fungsi dipanggil dengan akumulasi dan elemen berikutnya sampai urutannya selesai. Nama lain dari operasi ini adalah fold, karena urutan nilai dilipat menjadi satu hasil.
from functools import reduce
def kali(a, b):
return a * b
print(reduce(kali, [1, 2, 3, 4]))
print(reduce(kali, [1, 2, 3, 4], 10))
print(reduce(lambda a, b: a + b, [10, 20, 30], 100))24
240
160Perkalian tanpa nilai awal menghasilkan 24, sedangkan dengan nilai awal 10 hasilnya menjadi 240 karena akumulasinya memulai dari angka itu. Baris ketiga menunjukkan bentuk lambda untuk penjumlahan dengan nilai awal 100. Kebutuhan seperti menjumlah, mencari maksimum, atau menguji keberadaan sebaiknya memakai sum(), max(), dan any() yang sudah tersedia, sehingga reduce menyisakan pemakaian untuk pelipatan yang tidak punya padanan bawaan.
Perbandingan dengan comprehension#
Segala kerja map dan filter dapat ditulis ulang sebagai comprehension, dan hasilnya identik. Pembandingan berikut memeriksa kesetaraannya secara langsung, yaitu map(abs, ...) terhadap comprehension yang menghitung nilai mutlak dan filter terhadap comprehension bersyarat.
angka = [-4, 3, -1, 2, -5]
print(list(map(abs, angka)) == [abs(n) for n in angka])
print(list(filter(lambda n: n > 0, angka)) == [n for n in angka if n > 0])True
TrueKesetaraan itu berlaku juga untuk rangkaian yang lebih panjang. Dua bentuk berikut menyaring kelipatan tiga lalu mengubahnya menjadi string, dan keduanya menghasilkan nilai yang sama, meskipun membacanya menuntut usaha yang berbeda.
hasil_map_filter = list(map(str, filter(lambda n: n % 3 == 0, range(1, 11))))
hasil_comprehension = [str(n) for n in range(1, 11) if n % 3 == 0]
print(hasil_map_filter)
print(hasil_map_filter == hasil_comprehension)['3', '6', '9']
True| Kebutuhan | map / filter / reduce | comprehension |
|---|---|---|
| Mengubah elemen dengan fungsi yang sudah ada | map(str.upper, kata) | [s.upper() for s in kata] |
| Mengubah elemen dengan ekspresi singkat | map(lambda n: n * 2, data) | [n * 2 for n in data] |
| Menyaring dengan predikat | filter(predikat, data) | [n for n in data if predikat(n)] |
| Melipat menjadi satu nilai | reduce(fungsi, data) | tidak ada padanan satu baris |
Tabel di atas menunjukkan keputusan yang lazim diambil komunitas Python, yaitu comprehension lebih mudah dibaca ketika logikanya berupa ekspresi yang ditulis langsung, karena alurnya terbaca dari kiri ke kanan tanpa fungsi bantu. Fungsi map tetap unggul ketika fungsinya sudah tersedia dan namanya mewakili maksudnya, sedangkan reduce menangani pelipatan yang tidak memiliki padanan comprehension. Hasil map dan filter bersifat lazy sehingga pertimbangan memorinya setara dengan generator ekspresi, sedangkan reduce menghabiskan seluruh urutannya untuk menghasilkan satu nilai.
Kesalahan umum#
Kesalahan pertama adalah menganggap hasil map sebagai list yang dapat dipakai berulang. Objeknya iterator yang habis setelah dikonsumsi, sehingga konversi kedua menghasilkan daftar kosong tanpa error apa pun.
angka = [1, 2, 3]
m = map(str, angka)
print(list(m))
print(list(m))['1', '2', '3']
[]Kesalahan kedua adalah memanggil reduce tanpa nilai awal atas urutan yang mungkin kosong. Tanpa awal, fungsi ini menarik elemen pertama sebagai akumulasi, dan pada urutan kosong langkah itu tidak mungkin sehingga error TypeError muncul.
from functools import reduce
try:
reduce(lambda a, b: a + b, [])
except TypeError as e:
print(type(e).__name__, str(e))TypeError reduce() of empty iterable with no initial valuePerbaikannya adalah menyediakan nilai awal yang netral, misalnya 0 untuk penjumlahan dan 1 untuk perkalian, sehingga urutan kosong menghasilkan nilai netral itu juga. Kesalahan ketiga terjadi pada sisi keterbacaan, yaitu memaksakan map dengan lambda panjang padahal satu baris comprehension menyatakan maksudnya lebih jelas. Aturan yang sama juga membatasi reduce, karena pelipatan yang rumit lebih jelas ditulis sebagai perulangan dengan variabel akumulasi yang diberi nama.
Ringkasan#
mapmenerapkan fungsi pada setiap elemen danfiltermenyaring elemen yang lulus predikat, keduanya menghasilkan iterator lazy.reducedari modulfunctoolsmelipat urutan menjadi satu nilai melalui akumulasi, dengan nilai awal untuk keadaan kosong.- Semua hasil
mapdanfilterdapat ditulis ulang sebagai comprehension yang identik keluarannya. - Comprehension lebih mudah dibaca untuk ekspresi yang ditulis langsung, sedangkan
mapunggul untuk fungsi yang sudah ada danreduceuntuk pelipatan tanpa padanan.
Referensi#
- Built-in Functions — Python Software Foundation — Dokumentasi resmi map dan filter beserta perilaku lazy-nya.
- functools.reduce — Python Software Foundation — Referensi reduce termasuk peran nilai awal pada urutan kosong.
- Functional Programming HOWTO — Python Software Foundation — Panduan resmi yang menempatkan ketiga fungsi ini dalam gaya pemrograman fungsional Python.