into dan varian eager seperti mapv serta filterv menuntaskan evaluasi penuh dan mengembalikan koleksi konkret pada saat dipanggil. Topik ini membahas into sebagai pengalir seq ke koleksi target, mapv dan filterv sebagai padanan vector map dan filter, reduce atas vector, serta kriteria kapan evaluasi penuh lebih tepat daripada laziness.
into sebagai pengalir ke koleksi target#
into menerima koleksi target dan seq sumber, lalu mengalirkan setiap elemen sumber ke dalam target melalui conj. Tipe target menentukan tipe dan sifat hasilnya, sehingga satu sumber dapat dituangkan ke vector, set, maupun map tanpa mengubah logika alirannya.
(prn (into [] (map inc [1 2 3])))
(prn (into #{} (filter odd? (range 10))))
(prn (into {} [[:a 1] [:b 2]]))[2 3 4]
#{7 1 3 9 5}
{:a 1, :b 2}Target vector mempertahankan urutan elemen sumber, sedangkan target set menghilangkan duplikat dan urutan cetaknya mengikuti struktur internal set, terlihat dari hasil #{7 1 3 9 5}. Target map menerima pasangan kunci dan nilai, sehingga vector pasangan berubah menjadi map. Urutan argumennya dari target ke sumber, berlawanan dengan map yang meletakkan koleksi di akhir, dan sumbernya tidak harus koleksi karena seq apa pun dapat dialirkan. Bentuk penggunaan yang paling umum adalah mengumpulkan hasil map atau filter yang lazy menjadi koleksi konkret, misalnya (into [] (map inc [1 2 3])) yang menghasilkan [2 3 4].
mapv dan filterv#
mapv adalah padanan map yang sekaligus mengumpulkan hasilnya menjadi vector, sedangkan filterv melakukan hal yang sama untuk filter. Keduanya setara dengan menggabungkan map atau filter dengan into [].
(prn (mapv inc [1 2 3]))
(prn (filterv even? (range 10)))
(println (class (mapv inc [1 2 3])))[2 3 4]
[0 2 4 6 8]
clojure.lang.PersistentVectorHasilnya langsung berupa vector, terlihat dari tampilan cetak [2 3 4] dan dari class yang mengembalikan clojure.lang.PersistentVector. Tidak ada lazy sequence antara yang tersisa, sehingga jebakan realisasi tidak perlu diwaspadai pada hasil mapv dan filterv. Varian ini disediakan karena vector adalah bentuk hasil yang paling lazim diminta pemanggil, misalnya untuk akses indeks cepat atau integrasi dengan antarmuka yang menuntut urutan tetap.
Karena setiap elemen diproses seketika, urutan efek samping menjadi deterministik sesuai urutan sumbernya.
(prn (mapv (fn [x] (println "proses" x) x) [1 2 3]))proses 1
proses 2
proses 3
[1 2 3]Tiga pesan proses tercetak lebih dahulu, kemudian vector hasilnya dicetak. Pada map biasa, urutan antara efek samping dan pencetakan hasil tidak sesederhana itu karena realisasi baru terjadi pada saat pembacaan. Untuk alur kerja yang efek sampingnya harus selesai sebelum langkah berikutnya, varian eager memberikan kepastian yang tidak diberikan laziness.
reduce atas vector#
reduce tidak menghasilkan koleksi, melainkan merangkum vector menjadi satu nilai dalam satu kali penelusuran. Bentuk ini cocok untuk agregasi yang membutuhkan lebih dari satu ukuran sekaligus karena akumulatornya bebas berupa struktur apa pun.
(def nilai [78 55 92 67 88 45 73 81])
(def hasil
(reduce (fn [acc n]
{:total (+ (:total acc) n)
:jumlah (inc (:jumlah acc))})
{:total 0 :jumlah 0}
nilai))
(println hasil)
(println (/ (:total hasil) (:jumlah hasil))){:total 579, :jumlah 8}
579/8Akumulator berupa map yang menyimpan :total dan :jumlah, dan setiap elemen vector memperbarui keduanya. Satu kali penelusuran menghasilkan dua ukuran sekaligus tanpa koleksi antara, sehingga tidak perlu dua pipeline terpisah untuk penghitungan yang sama. Totalnya adalah 579 dari delapan nilai, sedangkan rata-ratanya dihitung dengan pembagian yang menghasilkan rasio 579/8.
Kapan eager mengungguli lazy#
Evaluasi penuh lebih tepat dalam tiga keadaan. Pertama, hasil akhir memang berupa koleksi konkret, misalnya vector yang dikirim ke antarmuka fungsi lain. Kedua, urutan dan waktu efek samping harus deterministik sejak saat dipanggil, seperti pencatatan yang wajib selesai sebelum langkah berikutnya. Ketiga, jebakan laziness perlu dihindari sama sekali, mulai dari memori yang tertahan hingga realisasi elemen yang berlebihan.
into juga menerima bentuk tiga argumen berupa transducer, yaitu transformasi yang tersusun tanpa koleksi antara.
(prn (into [] (filter odd?) (range 10)))
(prn (into [] (comp (filter odd?) (map #(* % %))) (range 10)))[1 3 5 7 9]
[1 9 25 49 81]Ekspresi (filter odd?) tanpa argumen koleksi menghasilkan transducer, dan comp menyusun dua transformasi menjadi satu langkah. Seluruh proses berjalan dalam satu kali penelusuran tanpa seq antara, sehingga hasilnya langsung berupa vector. Transducer dibahas lebih rinci pada topik Transducer di jalur ini.
Kebalikannya juga patut diwaspadai. Eager di tengah pipeline membangun koleksi antara yang segera dikonsumsi, sehingga untuk data besar laziness di tengah tetap lebih hemat dan pengumpulan cukup dilakukan sekali di ujung. Perpindahan dari lazy ke eager dengan demikian ditentukan oleh posisi tahap dalam alur, bukan kebiasaan bawaan.
Kesalahan umum#
- Mengira
into []menghilangkan duplikat. Target vector menerima seluruh elemen apa adanya. Gunakaninto #{}bila keunikan diperlukan, dengan konsekuensi urutan mengikuti struktur internal set. - Mengira semua target
intomenjaga urutan. Hanya koleksi terurut seperti vector yang mempertahankan urutan sumber, sedangkan set dan map tidak menjaminnya. - Menggunakan
mapvpada setiap tahap pipeline yang panjang. Setiapmapvmembangun vector antara yang langsung dikonsumsi tahap berikutnya. Tunda pengumpulannya ke tahap terakhir agar realisasi hanya terjadi sekali. - Menyeragamkan seluruh alur menjadi eager. Pengumpulan di setiap tahap membuang manfaat laziness, sedangkan hasil yang hanya dikonsumsi sekali cukup dirangkum dengan
reduce. Kriteria pemilihan tetap berdasarkan kebutuhan hasil akhir, bukan kebiasaan bawaan.
Ringkasan#
intomengalirkan elemen sumber ke koleksi target, dan tipe target menentukan tipe serta sifat hasilnya.mapvdanfiltervmengembalikan vector dengan evaluasi penuh dan efek samping yang deterministik.reducemerangkum vector dalam satu kali penelusuran dan akumulatornya dapat menampung beberapa ukuran sekaligus.- Evaluasi penuh dipilih ketika hasil akhir konkret, urutan efek penting, atau jebakan laziness perlu dihindari.
Referensi#
- clojure.core/into — ClojureDocs — dokumentasi into beserta bentuk tiga argumennya dengan transducer.
- clojure.core/mapv — ClojureDocs — dokumentasi mapv sebagai padanan eager map yang menghasilkan vector.
- clojure.core/reduce — ClojureDocs — dokumentasi reduce beserta contoh akumulator berupa struktur data.