Struktur data kompleks adalah struktur yang berisi struktur lain di dalamnya, misalnya array yang elemen-elemennya berupa hash. Topik ini membangun tiga bentuk yang paling sering digunakan, yaitu array dari hash, hash dari array, dan hash dari hash, beserta cara mengakses data yang bersarang.
Mengapa struktur data perlu bersarang#
Satu catatan data nyata jarang berupa satu nilai saja. Satu siswa memiliki nama dan nilai, satu hari memiliki beberapa nama piket, dan satu lokasi sensor memiliki pembacaan pada beberapa waktu. Kumpulan nilai yang menggambarkan satu objek semacam itu paling alami direpresentasikan sebagai hash, karena setiap kolomnya memiliki kunci yang bermakna.
Kunci mekanismenya ada pada sifat referensi, yaitu referensi berupa scalar sehingga dapat disimpan di mana pun scalar disimpan. Menyimpan referensi sebagai elemen array menghasilkan array dari hash, menyimpannya sebagai nilai hash menghasilkan hash dari array atau hash dari hash, dan kombinasi yang lebih dalam menjadi mungkin dengan pola yang sama. Tabel berikut merangkum tiga bentuk dasarnya.
| Bentuk | Isi penampung | Contoh pemakaian |
|---|---|---|
| array dari hash | setiap elemen berupa { nama => ..., nilai => ... } | daftar siswa beserta nilainya |
| hash dari array | setiap nilai berupa ["Ali", "Budi"] | jadwal piket untuk setiap hari |
| hash dari hash | setiap nilai berupa { pagi => ..., siang => ... } | pembacaan sensor per lokasi |
Pemilihan bentuk mengikuti makna datanya. Kumpulan rekaman yang dibaca berurutan cocok pada array, sedangkan pencarian berdasarkan nama seperti hari atau lokasi cocok pada hash. Referensi anonim membuat ketiga bentuk itu dapat dituliskan dalam satu ekspresi penugasan tanpa variabel perantara. Satu program juga boleh menggabungkan lebih dari dua tingkat, misalnya hash lokasi menuju hash waktu menuju daftar pembacaan, dan pola penulisannya tetap sama pada setiap tingkatnya.
Array dari hash#
Array dari hash menyimpan banyak rekaman yang sejenis dalam satu daftar. Setiap elemennya berupa referensi anonim menuju satu hash rekaman, sehingga satu array mampu memuat seluruh kelas tanpa perlu array paralel untuk setiap kolom. Urutan rekaman juga terjaga karena array menyimpan posisinya.
Skrip berikut membangun daftar kelas berisi tiga siswa, lalu menelusurinya satu per satu. Tanda koma setelah rekaman terakhir pada contoh boleh ditulis, dan kebiasaan itu memudahkan penambahan baris baru tanpa menyunting baris sebelumnya. Setiap rekaman tetap ditulis sebagai satu hash anonim agar batas antar siswa tidak hilang.
use strict;
use warnings;
# array dari hash: setiap elemen adalah referensi menuju hash satu siswa
my @kelas = (
{ nama => "Ali", nilai => 88 },
{ nama => "Budi", nilai => 75 },
{ nama => "Citra", nilai => 91 },
);
for my $siswa (@kelas) {
print "$siswa->{nama}: $siswa->{nilai}\n";
}Ali: 88
Budi: 75
Citra: 91Perulangan menampung setiap elemen pada $siswa, yang berupa referensi hash, lalu panah mengambil kolomnya dengan $siswa->{nama} dan $siswa->{nilai}. Menambah satu siswa berarti menambah satu elemen array berupa satu hash anonim, tanpa menyentuh baris lainnya. Apabila satu rekaman perlu kolom tambahan, misalnya kelas paralel, cukup kuncinya yang ditambah pada hash, dan pola penelusurannya tetap sama. Rekaman tunggal juga dapat dicapai tanpa perulangan, misalnya $kelas[0]{nilai} untuk nilai siswa pertama, karena panah di antara indeks array dan kurung kurawal sama-sama boleh ditiadakan. Penambahan dan penghapusan rekaman memanfaatkan fungsi array biasa, yaitu push, shift, dan splice, karena yang dikelola pada tingkat terluarnya tetap berupa array.
Hash dari array#
Hash dari array memetakan kunci berupa nama menuju daftar nilai yang panjangnya boleh berbeda antar kunci. Jadwal piket adalah contohnya, karena satu hari dapat dijaga satu orang dan hari lainnya dijaga tiga orang. Bentuk ini menggantikan kebiasaan membuat array terpisah untuk setiap hari. Kunci baru cukup diisi dengan satu referensi array anonim, dan seluruh isinya dapat dituliskan dalam satu penugasan. Kunci yang belum memiliki anggota sebaiknya diisi dengan daftar kosong berupa [] agar penelusurannya tidak perlu menangani nilai undef.
use strict;
use warnings;
# hash dari array: setiap nilai adalah referensi menuju daftar nama piket
my %piket = (
senin => ["Ali", "Budi"],
selasa => ["Citra"],
rabu => ["Dewi", "Ali", "Budi"],
);
for my $hari (sort keys %piket) {
print "$hari: ", join(", ", @{ $piket{$hari} }), "\n";
}
print "Piket pertama hari Senin: $piket{senin}[0]\n";rabu: Dewi, Ali, Budi
selasa: Citra
senin: Ali, Budi
Piket pertama hari Senin: AliPada baris terakhir skrip, akses $piket{senin}[0] menggabungkan dua tingkat sekaligus, yaitu kunci hash dan indeks array. Panah di antara keduanya boleh ditiadakan, sehingga $piket{senin}[0] dan $piket{senin}->[0] identik. Nilai hash diurai dengan @{ $piket{$hari} } karena nilai yang ditampung berupa referensi array, dan hasilnya langsung dapat disambung dengan join menjadi satu baris teks. Menambah anggota juga dilakukan melalui dereferensi yang sama, misalnya push @{ $piket{selasa} }, "Dewi"; menambah satu nama pada daftar hari Selasa tanpa menyentuh hari lainnya. Urutan anggota di dalam setiap daftar dijaga oleh array, sedangkan urutan kunci hash tidak dijamin, sehingga sort keys pada skrip menjaga keluarannya tetap rapi dan dapat diprediksi.
Hash dari hash#
Hash dari hash menyimpan matriks berlabel, yaitu baris dan kolom yang keduanya diakses melalui nama. Data sensor suhu cocok dengan bentuk ini, karena setiap lokasi memiliki kumpulan pembacaan per waktu, dan daftar waktunya boleh berbeda antar lokasi. Setiap kurung kurawal tambahan pada aksesnya menelusuri satu tingkat ke dalam, sehingga pola penulisannya memanjang mengikuti kedalaman data. Struktur ini tidak menuntut keseragaman, karena setiap hash dalam boleh memiliki kumpulan kunci yang berbeda dari hash dalam lainnya.
use strict;
use warnings;
# hash dari hash: setiap nilai adalah referensi menuju hash pembacaan
my %sensor = (
laboratorium => { pagi => 26.5, siang => 28.0 },
gudang => { pagi => 24.1, siang => 25.4 },
);
for my $lokasi (sort keys %sensor) {
for my $waktu (sort keys %{ $sensor{$lokasi} }) {
print "$lokasi, $waktu: $sensor{$lokasi}{$waktu} derajat\n";
}
}gudang, pagi: 24.1 derajat
gudang, siang: 25.4 derajat
laboratorium, pagi: 26.5 derajat
laboratorium, siang: 28 derajatPerulangan luar menelusuri lokasi, sedangkan perulangan dalam menelusuri kunci hash dalam melalui %{ $sensor{$lokasi} }. Akses penuhnya berupa $sensor{$lokasi}{$waktu}, yaitu dua kunci berturut-turut, dan panah di antara kurung kurawal juga boleh ditiadakan. Penambahan lokasi baru atau waktu baru cukup berupa penugasan ke kunci yang belum ada, sehingga struktur tumbuh mengikuti data yang ada. Bentuk ini juga menjadi dasar pohon data, yaitu struktur yang setiap cabangnya dapat diperluas dengan pola penugasan yang sama, dan cara mencetaknya dibahas pada topik Dereferensi. Kunci yang dibaca sebelum diisi menghasilkan undef, dan exists membedakan kunci yang belum ada dari nilai yang memang nol atau kosong.
Kesalahan umum#
Kesalahan yang lazim adalah membangun struktur bersarang dengan tanda kurung biasa alih-alih referensi anonim. Tanda kurung biasa hanya membuat daftar, dan Perl menggabungkan seluruh daftar yang bersarang menjadi satu array datar, sehingga batas antar baris hilang tanpa pesan error apa pun.
use strict;
use warnings;
# tanda kurung biasa menggabungkan seluruh daftar menjadi satu larik datar
my @matriks = (
(1, 2),
(3, 4),
);
print "Jumlah elemen: ", scalar(@matriks), "\n";
print "Elemen: @matriks\n";Jumlah elemen: 4
Elemen: 1 2 3 4Keluaran menunjukkan empat elemen berupa angka 1 sampai 4, dan tidak ada satu baris matriks pun yang tersisa. Perbaikannya adalah mengganti tanda kurung dalam dengan kurung siku, yaitu [1, 2] dan [3, 4], sehingga setiap baris menjadi referensi anonim yang utuh. Aturan yang sama berlaku pada hash, karena nilai hash juga wajib berupa referensi apabila ingin menampung struktur.
Ringkasan#
- Struktur bersarang terbentuk karena referensi berupa scalar, sehingga dapat disimpan sebagai elemen array maupun nilai hash.
- Array dari hash cocok untuk daftar rekaman sejenis, dan kolomnya diakses melalui
$elemen->{kunci}. - Hash dari array dan hash dari hash memetakan kunci menuju daftar atau rekaman, dengan akses bertingkat seperti
$piket{senin}[0]dan$sensor{$lokasi}{$waktu}. - Tanda kurung biasa menggabungkan daftar menjadi datar, sehingga struktur bersarang wajib dibangun dengan
[ ... ]dan{ ... }.
Referensi#
- perldsc — Perl documentation — Resep pembangunan struktur data kompleks beserta cara mengakses dan mencetaknya.
- perllol — Perl documentation — Pembahasan khusus daftar di dalam daftar sebagai pintu masuk struktur bersarang.
- perlreftut — Perl documentation — Dasar referensi yang menjadi fondasi seluruh struktur bersarang.