Array dan sekuens melengkapi list pada dua kebutuhan yang berbeda, yaitu data yang isinya perlu diubah secara langsung dan aliran data yang dihitung hanya saat diminta. Topik ini membahas penulisan array dalam [| |] beserta mutasinya lewat indeks, sekuens lazy dengan seq { ... }, konversi antar koleksi, dan tabel pemilihan kapan masing-masing unggul. Contoh menggunakan rekap nilai ujian dan aliran perhitungan yang seluruh keluarannya berasal dari eksekusi nyata.
Array ukurannya tetap dan isinya dapat diubah#
Array menyimpan sejumlah elemen bertipe sama dengan ukuran yang ditentukan saat dibuat dan tidak bertambah maupun berkurang. Berbeda dari list, isi array boleh diganti setelah array dibuat. Penulisannya menggunakan tanda [| dan |] dengan pemisah titik koma yang sama seperti list, dan elemennya diakses melalui indeks berbasis nol dengan notasi arr.[i].
// Array nilai ujian dan penggantian satu elemennya
let nilai = [| 80; 45; 90 |]
printfn "%A" nilai
printfn "elemen indeks 1 : %d" nilai.[1]
// mutasi satu elemen lewat indeks
nilai.[1] <- 95
printfn "setelah perbaikan : %A" nilai
printfn "jumlah elemen : %d" nilai.Length
printfn "total : %d" (Array.sum nilai)
printfn "rata-rata : %f" (Array.averageBy float nilai)[|80; 45; 90|]
elemen indeks 1 : 45
setelah perbaikan : [|80; 95; 90|]
jumlah elemen : 3
total : 265
rata-rata : 88.333333Baris nilai.[1] <- 95 mengganti isi satu sel array tanpa menyalin seluruh array, dan baris sesudahnya memperlihatkan nilai baru pada indeks yang sama. Penggantian semacam ini adalah mutasi yang sesungguhnya, bukan pembuatan array baru, sehingga setiap pemegang rujukan ke nilai membaca isi terbarunya. Properti .Length dan fungsi Array.sum menunjukkan bahwa modul Array menyediakan padanan hampir seluruh fungsi modul List, termasuk map, filter, dan sortBy, sehingga pengetahuan modul List langsung terpakai. Akses menurut indeks berlangsung konstan berapa pun jumlah elemennya karena elemen array ditempatkan berurutan di memori, dan sifat inilah yang membuat array unggul pada data yang sering diakses menurut posisi.
Sekuens lazy dengan seq#
Sekuens (sequence) adalah koleksi yang elemennya dihitung satu per satu pada saat diminta, bukan seluruhnya saat dibuat. Penulisannya menggunakan ekspresi seq { ... }, dan tipenya pada .NET adalah IEnumerable<'T> yang sama dengan antarmuka aliran data yang digunakan banyak pustaka .NET. Sifat evaluasi lambat ini disebut lazy, sehingga sekuens mampu mewakili aliran data panjang maupun tak hingga tanpa menampung seluruh isinya di memori.
// Sekuens lazy: elemen dihitung saat diminta, bukan saat dibuat
let kuadrat = seq {
for n in 1..3 do
printfn "menghitung kuadrat %d" n
yield n * n
}
printfn "sekuens sudah dibuat, belum ada hitungan yang berjalan"
kuadrat |> Seq.iter (printfn "hasil %d")
// enumerasi kedua menghitung ulang seluruh elemennya
kuadrat |> Seq.length |> printfn "jumlah elemen : %d"
// sekuens tak hingga tetap dapat dipotong
let genap = Seq.initInfinite (fun i -> i * 2)
printfn "%A" (genap |> Seq.take 5 |> Seq.toList)sekuens sudah dibuat, belum ada hitungan yang berjalan
menghitung kuadrat 1
hasil 1
menghitung kuadrat 2
hasil 4
menghitung kuadrat 3
hasil 9
menghitung kuadrat 1
menghitung kuadrat 2
menghitung kuadrat 3
jumlah elemen : 3
[0; 2; 4; 6; 8]Baris pertama keluaran muncul lebih dahulu, padahal penulisannya berada setelah definisi sekuens, karena pembuatan sekuens memang tidak menjalankan satu perhitungan pun. Setiap pasangan baris menghitung kuadrat dan hasil menunjukkan satu elemen dihitung tepat ketika Seq.iter memintanya. Baris keluaran sesudahnya memperlihatkan sifat yang sering mengejutkan, yaitu Seq.length yang menelusuri ulang sekuens dan memicu seluruh pesan menghitung kuadrat untuk kedua kalinya. Blok seq juga tidak dibatasi jumlah elemennya, dan Seq.initInfinite menghasilkan bilangan genap tak berujung yang tetap dapat dipotong menjadi lima elemen pertama oleh Seq.take.
Konversi antar koleksi#
Konversi antar koleksi memindahkan data dari satu bentuk ke bentuk lain dengan biaya penelusuran satu kali, dan setiap modul menyediakan fungsinya masing-masing. Konversi dari seq ke list atau array mematangkan (materialize) isinya, yaitu seluruh elemen sekuens dihitung seketika dan disimpan, sehingga sifat lambatnya hilang pada titik itu. Sebaliknya, konversi dari list atau array ke seq hanya membungkus koleksi yang sudah ada tanpa menyalin isinya.
// Konversi antar koleksi bawaan
let daftar = [ 1; 2; 3 ]
let larik = [| 4; 5 |]
let berurutan = seq { 1..4 }
printfn "%A" (daftar |> List.toArray)
printfn "%A" (larik |> Array.toList)
printfn "%A" (berurutan |> Seq.toList)
printfn "%A" (larik |> Seq.ofArray |> Seq.take 1 |> Seq.toList)[|1; 2; 3|]
[4; 5]
[1; 2; 3; 4]
[4]Empat baris keluaran di atas menunjukkan tanda pembatas yang ikut berganti sesuai bentuk tujuan, yaitu [| |] untuk array dan [ ] untuk list. Konversi terakhir memotong satu elemen dari array yang dibungkus sebagai sekuens, dan hasilnya baru disalin saat Seq.toList dipanggil. Konversi semacam ini lazim berada di tepi antar bagian program, misalnya data yang dibaca sebagai sekuens lalu dimatangkan menjadi array karena perlu diakses menurut indeks berulang kali.
Memilih antara list, array, dan seq#
Pilihan koleksi mengikuti cara data digunakan, bukan selera penulisan. Tabel berikut merangkum perbedaan ketiganya pada aspek yang paling menentukan.
| Aspek | list | array | seq |
|---|---|---|---|
| Isi | imutabel | dapat diganti lewat indeks | imutabel, sekali konsumsi per enumerasi |
| Evaluasi | seluruhnya saat dibuat | seluruhnya saat dibuat | lambat, per elemen saat diminta |
| Akses menurut indeks | berjalan dari kepala | langsung | berjalan berurutan |
| Situasi yang unggul | data tetap yang selalu dibaca dari awal | data besar yang sering diakses dan diubah menurut posisi | aliran data besar atau tak hingga, misalnya hasil pembacaan berkas panjang |
List tetap menjadi pilihan baku untuk data tetap berukuran kecil dan menengah karena imutabilitasnya menjaga alur program tetap terbaca. Array dipilih apabila mutasi menurut indeks memang inti prosesnya, misalnya penilaian yang diperbaiki satu per satu seperti pada contoh awal topik ini. Sekuens dipilih apabila data terlalu besar untuk ditampung seluruhnya atau pemrosesannya dapat berhenti sebelum akhir, karena elemen yang tidak diminta memang tidak pernah dihitung. Ketiga modulnya saling menyediakan padanan fungsi, sehingga langkah pipeline yang sama dapat dipindahkan antar koleksi dengan mengganti nama modulnya saja.
Ringkasan#
- Array ditulis dalam
[| |], ukurannya tetap, isinya dapat diganti lewatarr.[i] <- x, dan akses menurut indeksnya berlangsung konstan. - Sekuens
seq { ... }bersifat lazy, menghitung elemen per permintaan, menghitung ulang pada setiap enumerasi, dan mampu mewakili aliran tak hingga. - Konversi antar koleksi mematangkan data pada titik konversinya, sehingga konversi dari
seqsebaiknya ditempatkan di ujung pipeline. - Pemilihan koleksinya mengikuti cara data digunakan, yaitu list untuk data tetap, array untuk mutasi menurut indeks, dan seq untuk aliran besar.
Referensi#
- Arrays — Microsoft Learn
— Dokumentasi resmi array F#, termasuk penulisan
[| |], indeks, dan mutasi elemen. - Sequences — Microsoft Learn
— Dokumentasi resmi sekuens lazy beserta blok
seqdan fungsi modulSeq. - F# collection types — Microsoft Learn — Ringkasan seluruh koleksi bawaan F# beserta perbandingannya.