Data kompleks

Membangun hash dari hash, array dari hash, dan struktur bersarang lainnya dengan referensi anonim.

Daftar isi

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.

BentukIsi penampungContoh pemakaian
array dari hashsetiap elemen berupa { nama => ..., nilai => ... }daftar siswa beserta nilainya
hash dari arraysetiap nilai berupa ["Ali", "Budi"]jadwal piket untuk setiap hari
hash dari hashsetiap 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.

perl kelas-siswa.pl
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";
}
text Keluaran
Ali: 88
Budi: 75
Citra: 91

Perulangan 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.

perl piket-per-hari.pl
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";
text Keluaran
rabu: Dewi, Ali, Budi
selasa: Citra
senin: Ali, Budi
Piket pertama hari Senin: Ali

Pada 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.

perl sensor-lokasi.pl
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";
    }
}
text Keluaran
gudang, pagi: 24.1 derajat
gudang, siang: 25.4 derajat
laboratorium, pagi: 26.5 derajat
laboratorium, siang: 28 derajat

Perulangan 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.

perl daftar-datar.pl
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";
text Keluaran
Jumlah elemen: 4
Elemen: 1 2 3 4

Keluaran 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#