Dereferensi

Menelusuri struktur data bersarang, membentuk pohon data, dan mencetaknya dengan Data::Dumper.

Daftar isi

Dereferensi (dereference) adalah tindakan mengikuti alamat yang tersimpan pada referensi untuk mencapai data aslinya. Topik ini membahas penelusuran struktur bersarang dengan perulangan, penambahan dan penghapusan node, pencetakan seluruh struktur dengan Data::Dumper, serta perbedaan antara salinan data dan referensi menuju data yang sama.

Menelusuri struktur bersarang#

Penelusuran struktur bersarang menggabungkan dua pola perulangan yang sudah dikenal, yaitu foreach my $x (@$ref) untuk referensi array dan foreach my $k (sort keys %$ref) untuk referensi hash. Kedua pola itu dipasangkan berlapis mengikuti kedalaman struktur, dan panah mengambil kolom yang diperlukan pada setiap tingkat. Kunci hash sebaiknya diurutkan dengan sort pada setiap tingkat agar urutan kunjungannya tidak berubah-ubah antar eksekusi.

Skrip berikut memuat array dari hash yang salah satu kolomnya kembali berupa array. Perulangan luarnya menelusuri seluruh siswa melalui @$nilai, sedangkan perulangan dalamnya menjumlahkan nilai tugas melalui @{ $siswa->{tugas} }. Penguraian berkurung pada @{ $siswa->{tugas} } diperlukan karena isinya merupakan ekspresi panah dan bukan sekadar satu nama variabel.

perl tabel-nilai.pl
use strict;
use warnings;

# array dari hash dengan kolom tugas berupa referensi array
my $nilai = [
    { nama => "Ali",  tugas => [80, 85, 90] },
    { nama => "Budi", tugas => [70, 75] },
];

for my $siswa (@$nilai) {
    my $total = 0;
    for my $n (@{ $siswa->{tugas} }) {
        $total += $n;
    }
    my $rata = $total / scalar @{ $siswa->{tugas} };
    printf "%s: rata-rata %.1f\n", $siswa->{nama}, $rata;
}
text Keluaran
Ali: rata-rata 85.0
Budi: rata-rata 72.5

Setiap tingkat pada skrip menunjukkan satu bentuk dereferensi yang berbeda, yaitu @$nilai untuk array terluar, $siswa->{tugas} untuk kolom hash, dan @{ ... } untuk mengurai kembali referensi array tersebut. Struktur bersarang menjadi mudah dibaca apabila setiap tingkatnya dinamai dengan variabel perulangan yang bermakna, daripada digabungkan dalam satu ekspresi akses yang panjang. Variabel bernama juga mempermudah pelacakan error karena nilainya dapat dicetak pada titik mana pun. Apabila salah satu tingkat ternyata belum terisi, error dereferensi menyebut baris aksesnya, dan Data::Dumper pada bagian berikutnya membantu memastikan bentuk struktur sebelum logika penelusurannya disempurnakan.

Menambah dan menghapus node#

Struktur bersarang bersifat dinamis karena node dapat ditambah dan dihapus saat program berjalan. Penambahan node hash berupa penugasan ke kunci baru, penambahan anggota pada node array menggunakan push dengan dereferensi, dan penghapusan node hash menggunakan delete. Untuk node berupa array, pengurangan anggotanya menggunakan splice, shift, atau pop. delete mengembalikan nilai kunci yang dihapusnya, sehingga node yang dibuang masih dapat ditampung dahulu bila isinya diperlukan.

Skrip berikut mengelola jadwal piket yang tersimpan sebagai hash dari array, dengan tiga jenis perubahan sekaligus. Titik masuk ketiganya sama, yaitu variabel referensi $piket, sehingga satu struktur yang sama ditambah, diperluas, dan dikurangi.

perl tambah-hapus-node.pl
use strict;
use warnings;

my $piket = {
    senin  => ["Ali", "Budi"],
    selasa => ["Citra"],
};

# menambah node baru berupa kunci hari
$piket->{rabu} = ["Dewi", "Ali"];

# menambah anggota pada node yang sudah ada
push @{ $piket->{senin} }, "Citra";

# menghapus node dengan delete
delete $piket->{selasa};

for my $hari (sort keys %$piket) {
    print "$hari: ", join(", ", @{ $piket->{$hari} }), "\n";
}
text Keluaran
rabu: Dewi, Ali
senin: Ali, Budi, Citra

Keluaran menunjukkan rabu muncul sebagai node baru, senin bertambah satu anggota, dan selasa tidak ada lagi pada daftar kunci. Seluruh perubahan dilakukan melalui satu variabel referensi $piket, sehingga struktur di memori berubah tanpa perlu membangun ulang struktur dari awal. Pola inilah yang digunakan program untuk menampung data yang datang bertahap, misalnya hasil pembacaan berkas atau masukan pengguna. Perl juga membentuk node yang belum ada secara otomatis ketika nilai ditulis ke kedalamannya, sehingga penugasan berupa $piket->{kamis}[0] = "Dewi"; menciptakan kunci kamis beserta arraynya sekaligus.

Mencetak struktur dengan Data::Dumper#

Struktur bersarang yang dalam sulit diperiksa melalui cetakan per baris. Modul inti Data::Dumper menyediakan fungsi Dumper() yang mengubah struktur apa pun menjadi teks berformat Perl, sehingga seluruh isinya dapat diperiksa sekaligus beserta tingkat kedalamannya. Fungsi ini lazim digunakan saat pengembangan untuk memastikan struktur yang terbentuk sesuai rancangan. Modul ini termasuk inti Perl, sehingga tersedia pada setiap pemasangan dan tidak memerlukan instalasi terpisah.

perl tampilkan-dumper.pl
use strict;
use warnings;
use Data::Dumper;

my $kelas = {
    siswa => [
        { nama => "Ali",   nilai => 88 },
        { nama => "Citra", nilai => 91 },
    ],
    piket => {
        senin => ["Ali", "Budi"],
    },
};

$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Indent   = 1;
print Dumper($kelas);
text Keluaran
$VAR1 = {
  'piket' => {
    'senin' => [
      'Ali',
      'Budi'
    ]
  },
  'siswa' => [
    {
      'nama' => 'Ali',
      'nilai' => 88
    },
    {
      'nama' => 'Citra',
      'nilai' => 91
    }
  ]
};

Dua variabel paket diatur sebelum pencetakan. $Data::Dumper::Sortkeys = 1; mengurutkan seluruh kunci hash sehingga keluaran sama di setiap eksekusi, sedangkan $Data::Dumper::Indent = 1; memilih format lekukan yang ringkas. Tanpa pengurutan, urutan kunci mengikuti susunan internal hash yang berubah-ubah, sehingga perbandingan keluaran antar eksekusi tidak dapat diandalkan. Fungsi Dumper() juga menerima lebih dari satu argumen, dan setiap argumennya dilabeli $VAR1, $VAR2, dan seterusnya. Apabila struktur yang diperiksa sangat besar, $Data::Dumper::Maxdepth membatasi kedalaman pencetakan sehingga bagian terluarnya tetap terbaca.

Referensi dan salinan#

Penugasan array ke array baru menyalin isinya, sedangkan penugasan referensi hanya menyalin alamatnya. Perbedaan itu menentukan apakah perubahan pada satu sisi terlihat di sisi lainnya. Array yang disalin berdiri sendiri, sedangkan data yang dicapai melalui referensi tetap data yang sama dengan milik pemilik referensinya. Penyalinan yang hanya meniru tingkat terluarnya disebut salinan dangkal (shallow copy), sedangkan penyalinan sampai tingkat terdalam disebut salinan mendalam (deep copy).

Skrip berikut membandingkan keduanya pada array yang sama, dengan satu referensi dan satu salinan.

perl salinan-vs-referensi.pl
use strict;
use warnings;

my @asal = (10, 20, 30);

my $ref  = \@asal;   # referensi menunjuk array yang sama
my @copy = @asal;    # salinan berdiri sendiri

push @$ref, 40;
push @copy, 99;

print "Array asal: @asal\n";
print "Salinan:    @copy\n";
text Keluaran
Array asal: 10 20 30 40
Salinan:    10 20 30 99

Keluaran memperlihatkan kedua sisi, yaitu push melalui $ref menambah elemen pada @asal, sedangkan push pada @copy tidak menyentuhnya. Perbedaan ini berlaku pada struktur bersarang juga, karena menyalin array yang berisi referensi hanya menyalin alamat-alamatnya, dan struktur dalam yang ditunjuk tetap data yang sama. Penyalinan yang benar-benar terpisah sampai ke struktur dalamnya memerlukan prosedur khusus, dan bukan sekadar penugasan satu baris. Modul inti Storable menyediakan fungsi dclone untuk keperluan itu, dan hasilnya berupa struktur baru yang seluruh tingkatnya berdiri sendiri.

Kesalahan umum#

Sigil pada dereferensi menyatakan jenis data yang diharapkan, dan sigil yang tidak sesuai dengan isi referensinya memicu error saat program berjalan. Kesalahan ini sering muncul ketika struktur yang ditampung berubah jenis, misalnya hash yang kemudian diperlakukan sebagai array.

perl Contoh: dereferensi hash dengan sigil array
# tidak-dijalankan
use strict;
use warnings;

my %piket = (senin => ["Ali", "Budi"]);
my $r = \%piket;

my @daftar = @$r;   # dereferensi hash seolah array

print "Jumlah elemen: ", scalar(@daftar), "\n";
text Pesan error yang muncul
Not an ARRAY reference at salah-sigil.pl line 7.

Pesan error menyebutkan baris dereferensinya, sehingga sumbernya mudah dilacak. Perhatikan bahwa $r pada contoh tetap benar sebagai referensi hash, dan kesalahannya hanya terletak pada sigil dereferensinya, sehingga mengganti @$r menjadi %$r sudah cukup. Kebiasaan pencegahannya adalah memeriksa jenis referensi dengan ref() terlebih dahulu apabila jenisnya tidak pasti, lalu memilih sigil yang sesuai dengan hasil pemeriksaan itu.

Ringkasan#

  • Penelusuran bersarang menggabungkan @$ref untuk array dan keys %$ref untuk hash pada tingkat yang berlapis.
  • Node ditambah melalui penugasan kunci baru atau push melalui dereferensi, dan dihapus dengan delete untuk kunci hash.
  • Data::Dumper mencetak seluruh struktur, dan $Data::Dumper::Sortkeys = 1; menjaga keluarannya tetap stabil antar eksekusi.
  • Penugasan array menyalin isinya, sedangkan referensi membagi alamat yang sama sehingga perubahannya terlihat dari dua sisi.

Referensi#