List adalah koleksi imutabel bawaan F# yang menyimpan elemen bertipe sama dalam urutan tetap. Topik ini membahas penulisan list dalam kurung siku, operator kons :: dan penggabungan @, pemahaman list (list comprehension), serta fungsi-fungsi modul List untuk transformasi data. Pembahasan ditutup dengan struktur kepala-ekor di balik list beserta implikasinya terhadap biaya penambahan elemen. Setelah topik ini dibaca, penulisan list dan pemilihan operasinya dapat dilakukan dengan dasar yang jelas.
List ditulis dalam kurung siku#
List F# ditulis sebagai deretan nilai yang dipisahkan tanda titik koma di dalam tanda kurung siku, misalnya [ 80; 45; 90; 60 ]. Seluruh elemen pada satu list wajib bertipe sama karena compiler memeriksa keseragamannya saat pemeriksaan tipe. Tipe nilai pada contoh berikut tersimpulkan sebagai int list tanpa satu pun anotasi tipe. Selain ditulis satu per satu, list berisi bilangan berurutan cukup ditulis sebagai rentang [awal..akhir], sedangkan list yang elemennya dihitung dari pola ditulis sebagai pemahaman list berbentuk [ for ... ].
// Literal list, rentang, dan pemahaman list
let nilai = [ 80; 45; 90; 60 ]
printfn "%A" nilai
// rentang 1..5 menghasilkan list berurutan
printfn "%A" [ 1..5 ]
// pemahaman list: kuadrat dari setiap bilangan genap
let kuadrat =
[ for n in 1..6 do
if n % 2 = 0 then yield n * n ]
printfn "%A" kuadrat[80; 45; 90; 60]
[1; 2; 3; 4; 5]
[4; 16; 36]Pemformat %A menampilkan isi list lengkap dengan tanda kurung dan pemisahnya, sehingga isi list mudah diperiksa sekilas. Rentang [ 1..5 ] menghasilkan lima elemen berurutan tanpa penulisan satu per satu. Pemahaman list pada kuadrat menelusuri bilangan 1 sampai 6, menyaring yang genap dengan klausul if, lalu menghasilkan kuadratnya melalui kata yield. Ketiga penulisan di atas menghasilkan list yang setara secara isi, sehingga pilihannya murni soal keterbacaan, bukan soal perilaku.
Menambah elemen dengan :: dan @#
Operator :: (cons) menambahkan satu elemen di depan list dan menghasilkan list baru, sedangkan operator @ menggabungkan dua list menjadi satu. Kedua operator tersebut tidak pernah mengubah list yang ada karena list bersifat imutabel. Hasilnya selalu list baru yang dapat diikat dengan let, dan list lama tetap dapat digunakan sebagaimana adanya.
// Operator :: menambah satu elemen di depan list
let antrian = [ 2; 3; 4 ]
let antrianBaru = 1 :: antrian
printfn "%A" antrianBaru
printfn "list lama tetap utuh: %A" antrian
// Operator @ menggabungkan dua list menjadi satu
printfn "%A" ([ 1; 2 ] @ [ 3; 4 ])
// rantai kons yang diakhiri list kosong sama dengan penulisan literal
printfn "%b" ([ 1; 2; 3 ] = 1 :: 2 :: 3 :: [])[1; 2; 3; 4]
list lama tetap utuh: [2; 3; 4]
[1; 2; 3; 4]
trueBaris 1 :: antrian membuat list baru yang elemen pertamanya 1 dan sisanya seluruh isi antrian. Baris kedua keluaran menunjukkan bahwa antrian tidak bergeser sama sekali akibat operasi tersebut. Operator :: selalu menyambungkan satu elemen dengan satu list, sehingga ruas kanannya wajib berupa list, sedangkan @ menyambungkan dua list dan menghasilkan list gabungannya. Pemeriksaan terakhir memperlihatkan bahwa literal [ 1; 2; 3 ] dan rantai kons 1 :: 2 :: 3 :: [] bernilai sama, dan bentuk rantai itulah yang sesungguhnya menjadi representasi internal list F#.
Modul List untuk transformasi#
Modul List menghimpun fungsi transformasi yang bekerja pada list tanpa mengubah list aslinya. Fungsi map mengubah setiap elemen, filter menyaring elemen berdasarkan predikat, dan sortBy mengurutkan elemen menurut kunci yang dihitung dari setiap elemen. Untuk agregasi, fungsi sum dan length menghitung total serta banyaknya elemen, sedangkan head dan tail mengambil elemen pertama serta sisanya. Contoh berikut menerapkan fungsi-fungsi tersebut pada rekap nilai ujian.
// Rekap nilai ujian dengan fungsi modul List
let nilai = [ 80; 45; 75; 90; 60 ]
printfn "jumlah elemen : %d" (List.length nilai)
printfn "total nilai : %d" (List.sum nilai)
printfn "nilai pertama : %d" (List.head nilai)
printfn "sisanya : %A" (List.tail nilai)
let dinilaiUlang = nilai |> List.map (fun n -> n + 5)
printfn "naik 5 : %A" dinilaiUlang
let lulus = nilai |> List.filter (fun n -> n >= 75)
printfn "lulus : %A" lulus
let terurut = nilai |> List.sortBy (fun n -> -n)
printfn "terurut : %A" terurutjumlah elemen : 5
total nilai : 350
nilai pertama : 80
sisanya : [45; 75; 90; 60]
naik 5 : [85; 50; 80; 95; 65]
lulus : [80; 75; 90]
terurut : [90; 80; 75; 60; 45]Setiap fungsi pada contoh mengembalikan list baru, sedangkan nilai tetap berisi [ 80; 45; 75; 90; 60 ] dari awal hingga akhir. Hasil List.map dan List.filter disimpan ke nama baru karena operasi tanpa pengikatan tidak meninggalkan efek apa pun. Penataan data pada argumen terakhir membuat fungsi-fungsi tersebut cocok dipanggil melalui operator pipeline |>, sebagaimana nilai |> List.filter (fun n -> n >= 75) yang membaca data lebih dahulu sebelum memprosesnya. Fungsi List.sortBy menerima kunci pengurutan, dan kunci (fun n -> -n) pada contoh menghasilkan urutan menurun tanpa fungsi tambahan.
Struktur kepala-ekor dan implikasinya#
List F# disusun sebagai rantai simpul (node) yang masing-masing menyimpan satu elemen beserta sambungan ke ekor, yaitu list sisanya. List [ 1; 2; 3 ] berarti simpul berisi 1 yang menunjuk ke simpul berisi 2, dan seterusnya sampai list kosong [] sebagai penutupnya. Kepala list adalah elemen pertama, sedangkan ekornya adalah list berisi seluruh elemen sesudah kepala. Bentuk inilah yang membuat List.head dan List.tail dapat diperoleh tanpa menelusuri seluruh isi list.
Struktur tersebut menentukan biaya setiap operasinya. Menambah satu elemen di depan dengan :: hanya membuat satu simpul baru yang ekornya menunjuk ke list lama, sehingga biayanya tetap berapa pun panjang listnya, dan isi list lama tetap digunakan bersama tanpa disalin. Sebaliknya, @ menyalin seluruh simpul pada list ruas kirinya, sehingga biayanya membesar mengikuti panjang list kiri, sedangkan list ruas kanan cukup digunakan ulang. Konsekuensinya, penambahan elemen dilakukan di depan, dan penggabungan disusun agar list terpanjang berada di ruas kanan apabila urutannya tidak masalah. Akses elemen menurut indeks juga berjalan dari kepala satu simpul demi satu simpul, sehingga untuk pencarian berdasarkan posisi, array lebih tepat.
Kesalahan umum#
Kesalahan yang paling sering terjadi pada list adalah mencampur tipe yang berbeda di dalam satu list. List F# menuntut keseragaman tipe, sehingga penggabungan list bilangan dengan list teks ditolak compiler sebelum program berjalan. Berkas berikut sengaja memuat penggabungan semacam itu untuk memperlihatkan reaksi compiler.
// tidak-dijalankan: berkas ini sengaja menggabungkan list bertipe berbeda
let angka = [ 1; 2 ]
let huruf = [ "a"; "b" ]
let campuran = angka @ huruf
printfn "%A" campuranPerintah berikut menjalankan berkas tersebut. Pesan error yang muncul memang diharapkan, dan bagian jalur folder pada pesan disingkirkan agar ringkas.
# berkas sengaja gagal dikompilasi, lalu jalur folder disingkirkan dari pesan
dotnet fsi gabung-salah.fsx 2>&1 | sed "s|.*/||"gabung-salah.fsx(4,24): error FS0001: Type mismatch. Expecting a
'int list'
but given a
'string list'
The type 'int' does not match the type 'string'Pesan error FS0001 menyatakan bahwa ruas kanan @ diharapkan bertipe int list, sedangkan huruf bertipe string list. Compiler menolak skripnya sebelum satu baris pun dijalankan, sehingga printfn pada baris terakhir tidak pernah dieksekusi. Perbaikannya menyatukan tipe terlebih dahulu, misalnya mengubah angka menjadi teks dengan List.map string sebelum penggabungan, atau menyimpan data campuran sebagai tuple maupun record. Kesalahan lain yang senada adalah memanggil List.head atau List.tail pada list yang mungkin kosong, dan pemecahannya sudah dibahas pada catatan bagian sebelumnya.
Ringkasan#
- List imutabel bertipe homogen ditulis dalam kurung siku, dengan rentang
[1..5]dan pemahaman list[ for ... ]sebagai penulisan singkat. - Operator
::menambah satu elemen di depan tanpa menyalin list lama, sedangkan@menyalin seluruh simpul pada list ruas kirinya. - Modul
Listmenyediakanmap,filter,sortBy,sum,length,head, dantailyang semuanya mengembalikan list atau nilai baru tanpa mengubah list aslinya. - Struktur kepala-ekor membuat penambahan di depan berbiaya tetap, sedangkan akses menurut indeks dan penggabungan di ujung belakang berbiaya sebanding dengan panjang list.
Referensi#
- Lists — Microsoft Learn
— Dokumentasi resmi tipe list F#, termasuk rentang, pemahaman list, dan operator
::serta@. - F# collection types — Microsoft Learn — Ringkasan koleksi bawaan F# dan posisi list di antara koleksi lainnya.
- F# Language Reference — Microsoft Learn — Rujukan induk sintaks bahasa F# yang menaungi seluruh topik di jalur ini.