Subrutin (subroutine) adalah blok kode bernama yang dapat dipanggil berulang kali dari bagian mana pun dalam program. Pemecahan program menjadi subrutin membuat satu logika cukup ditulis satu kali, lalu dijalankan ulang dengan data yang berbeda pada setiap pemanggilan. Topik ini membahas cara mendefinisikan subrutin dengan kata kunci sub, cara argumen masuk melalui array khusus @_, dua bentuk pemanggilan dengan dan tanpa tanda kurung, serta hubungan @_ dengan variabel asli di pemanggil.
Mendefinisikan subrutin#
Subrutin didefinisikan dengan kata kunci sub diikuti nama dan blok kurung kurawal yang memuat pernyataan-pernyataannya. Nama subrutin mengikuti aturan pengenal yang sama dengan nama variabel, yaitu gabungan huruf, angka, dan garis bawah yang tidak dimulai dengan angka. Nama subrutin disimpan pada slot terpisah dalam tabel simbol paket, sehingga subrutin dan variabel boleh memiliki nama yang sama tanpa saling menimpa. Perl 5.40 juga menyediakan tanda tangan subrutin (signature) yang ditulis di dalam kurung setelah nama, dan fitur ini diaktifkan melalui use feature 'signatures' atau use v5.36. Isi blok tidak dijalankan pada saat definisinya dibaca, melainkan setiap kali subrutin dipanggil, sehingga satu blok logika dapat melayani banyak pemanggilan dengan data yang berbeda.
Skrip berikut mendefinisikan subrutin cetak_nilai yang menerima nama siswa dan nilai ujian, lalu memanggilnya dua kali dengan pasangan argumen yang berbeda. Di dalam blok, argumen diurai ke variabel leksikal $nama dan $nilai dengan mekanisme yang dijelaskan pada bagian berikutnya.
use strict;
use warnings;
sub cetak_nilai {
my ($nama, $nilai) = @_;
print "$nama memperoleh nilai $nilai\n";
}
cetak_nilai("Ali", 88);
cetak_nilai("Citra", 91);Ali memperoleh nilai 88
Citra memperoleh nilai 91Keluaran menunjukkan blok yang sama dieksekusi dua kali, dengan argumen yang berbeda pada setiap pemanggilan. Pemisahan logika seperti ini membuat perubahan cukup dilakukan di satu tempat, yaitu di dalam subrutin, dan hasilnya berlaku untuk seluruh pemanggilan tanpa perlu menyunting baris pemanggilnya. Definisi juga boleh diletakkan di akhir berkas karena compiler membaca seluruh program terlebih dahulu sebelum eksekusi dimulai, sehingga penataan kode dapat mengikuti urutan alur utamanya.
Menerima argumen melalui @_#
Argumen yang dikirim pemanggil masuk ke subrutin berupa satu daftar pada array khusus @_. Array ini terbentuk pada setiap pemanggilan dan isinya adalah seluruh argumen dalam urutan pengirimannya. Subrutin tidak memiliki parameter bernama, sehingga posisi argumenlah yang menentukan maknanya, dan penguraiannya menjadi tanggung jawab kode di dalam subrutin. Isinya juga tidak tersisa dari pemanggilan sebelumnya karena Perl menyediakan @_ yang terpisah untuk setiap pemanggilan, termasuk ketika subrutin memanggil subrutin lain di dalam tubuhnya.
Cara penguraian yang paling umum adalah penugasan daftar berupa my ($a, $b) = @_;, yang memindahkan elemen @_ ke variabel leksikal sekaligus. Argumen yang lebih banyak daripada variabel dibiarkan terpotong, sedangkan argumen yang kurang menghasilkan undef. Alternatifnya adalah shift, yang mengambil satu elemen dari depan @_ sekaligus mengurangi isinya, sehingga cocok untuk argumen yang diproses satu per satu. Apabila argumen berupa pasangan kunci dan nilai, penugasan berbentuk my %opsi = @_; menampungnya langsung menjadi hash.
use strict;
use warnings;
# mengurai seluruh @_ sekaligus melalui penugasan daftar
sub rata_rata {
my ($a, $b, $c) = @_;
my $jumlah = $a + $b + $c;
print "Rata-rata: ", $jumlah / 3, "\n";
}
# mengambil argumen satu per satu dengan shift
sub cetak_piket {
while (my $nama = shift) {
print "Piket: $nama\n";
}
}
rata_rata(80, 90, 100);
cetak_piket("Ali", "Budi", "Citra");Rata-rata: 90
Piket: Ali
Piket: Budi
Piket: CitraSubrutin rata_rata mengurai seluruh argumennya sekaligus karena ketiganya diperlukan bersamaan dalam satu rumus. Subrutin cetak_piket menggunakan pola while dengan shift karena jumlah argumennya tidak ditentukan di muka, dan perulangan berhenti seketika ketika @_ sudah kosong karena shift mengembalikan undef. Kedua pola itu juga dapat digabung, misalnya shift untuk argumen wajib di depan dan penugasan daftar untuk sisanya. Apabila salah satu argumen wajib tidak terisi, undef hasil penugasannya dapat diperiksa dengan defined sebelum data diproses lebih lanjut.
Memanggil subrutin#
Subrutin dipanggil dengan menuliskan namanya beserta argumen di dalam tanda kurung, contohnya cetak_nilai("Ali", 88). Perl juga mengizinkan pemanggilan tanpa tanda kurung, contohnya cetak_nilai "Ali", 88, dan kedua bentuk itu identik untuk subrutin buatan sendiri. Bentuk tanpa tanda kurung menuntut satu syarat, yaitu compiler sudah mengenal subrutin tersebut ketika baris pemanggilannya dibaca, sehingga definisinya perlu berada sebelum pemanggilan. Pada kedua bentuk, argumen dikirim berupa satu daftar nilai yang terurai, sehingga array yang dikirim langsung tergabung ke dalam daftar argumen tanpa tanda batas wadahnya.
Skrip berikut memanggil satu subrutin yang sama dengan dua bentuk pemanggilan, dan keluarannya identik. Perbedaannya hanya terletak pada tanda kurung, sedangkan daftar argumen yang sampai ke subrutin sama persis. Bentuk tanpa kurung pada skrip ditulis setelah definisinya, sehingga syarat pengenalannya sudah terpenuhi.
use strict;
use warnings;
sub sapa {
my ($nama) = @_;
print "Halo, $nama\n";
}
# pemanggilan dengan tanda kurung
sapa("Dewi");
# pemanggilan tanpa tanda kurung
sapa "Ali";Halo, Dewi
Halo, AliPemilihan bentuk merupakan perkara keterbacaan. Tanda kurung memperjelas batas argumen pada baris yang panjang, sedangkan bentuk tanpa tanda kurung membuat baris terasa seperti pernyataan bawaan Perl. Banyak fungsi bawaan juga lazim dipanggil tanpa tanda kurung, contohnya push @daftar, 5 dan print "teks", sehingga bentuk ini bukan hal asing dalam bahasa ini. Pada satu proyek, salah satu bentuk dipilih lalu dipertahankan secara konsisten. Tanda kurung juga membuat pemanggilan dikenali compiler pada saat kompilasi, sehingga subrutin yang didefinisikan di akhir berkas tetap dapat dipanggil dari baris mana pun dengan bentuk ini.
Hubungan @_ dengan argumen asli#
Elemen @_ bukan salinan, melainkan alias dari argumen aslinya. Apabila subrutin menulis ke elemen @_, misalnya $_[0] += 10, variabel di pemanggil ikut berubah karena keduanya menunjuk penyimpanan yang sama. Perilaku ini diinginkan untuk subrutin yang memang sengaja mengubah argumennya, tetapi menjadi sumber kesalahan halus apabila perubahan terjadi tanpa sengaja.
Skrip berikut memperlihatkan efek alias tersebut. Subrutin tambah_poin menambah elemen pertama @_ dengan sepuluh, dan nilai pada variabel pemanggil ikut naik tanpa pernyataan penugasan apa pun di sisi pemanggil.
use strict;
use warnings;
# elemen @_ merupakan alias dari argumen aslinya
sub tambah_poin {
$_[0] += 10;
}
my $nilai = 72;
tambah_poin($nilai);
print "Nilai setelah pemanggilan: $nilai\n";Nilai setelah pemanggilan: 82Perilaku alias melekat pada setiap nilai yang dikirim, tetapi tidak pada wadahnya. Menambah, menghapus, atau mengurutkan elemen @_ hanya mengubah daftar argumen di dalam subrutin, dan array atau hash asal di pemanggil tetap utuh, karena yang dikirim berupa daftar nilai terurai dan bukan wadahnya. Untuk benar-benar mengubah array atau hash milik pemanggil, referensi diperlukan, dan mekanismenya dibahas pada topik Referensi.
Kesalahan umum#
Kesalahan pertama adalah memanggil subrutin tanpa tanda kurung sebelum compiler mengenalnya. Apabila pemanggilan tanpa kurung berada di atas definisinya, compiler memperlakukan nama itu sebagai kata liar (bareword) dan menghentikan proses kompilasi. Karena kesalahannya terjadi saat kompilasi, tidak ada satu baris pun yang sempat dijalankan.
# tidak-dijalankan
use strict;
use warnings;
mulai;
sub mulai {
print "Program dimulai\n";
}Bareword "mulai" not allowed while "strict subs" in use at panggilan-awal.pl line 4.
Execution of panggilan-awal.pl aborted due to compilation errors.Perbaikannya cukup menambahkan tanda kurung pada pemanggilan, karena pemanggilan berbentuk mulai() tetap dikenali meskipun definisinya berada di bawah, atau memindahkan definisi subrutin ke atas pemanggilannya.
Kesalahan kedua adalah lupa mendeklarasikan variabel kerja dengan my di dalam subrutin. Di bawah use strict, setiap variabel wajib dideklarasikan, dan variabel yang ditulis begitu saja menghentikan kompilasi dengan pesan pada setiap baris pemakaiannya.
# tidak-dijalankan
use strict;
use warnings;
sub rata_rata {
my ($a, $b) = @_;
$jumlah = $a + $b;
print "Rata-rata: ", $jumlah / 2, "\n";
}
rata_rata(8, 9);Global symbol "$jumlah" requires explicit package name (did you forget to declare "my $jumlah"?) at variabel-tanpa-my.pl line 6.
Global symbol "$jumlah" requires explicit package name (did you forget to declare "my $jumlah"?) at variabel-tanpa-my.pl line 7.
Execution of variabel-tanpa-my.pl aborted due to compilation errors.Dua baris error muncul karena $jumlah digunakan pada dua tempat, yaitu penugasan dan pencetakan. Deklarasi my $jumlah = $a + $b; menyelesaikannya sekaligus menjaga variabel itu tetap lokal pada subrutin, sehingga tidak bertabrakan dengan variabel lain yang bernama sama di bagian program yang berbeda.
Ringkasan#
- Subrutin didefinisikan dengan
sub nama { ... }dan isinya baru dijalankan setiap kali dipanggil. - Seluruh argumen masuk sebagai satu daftar pada
@_, lalu diurai dengan penugasan daftar sepertimy ($a, $b) = @_;atau satu per satu denganshift. - Pemanggilan
nama(argumen)dannama argumenidentik, tetapi bentuk tanpa tanda kurung menuntut subrutin sudah dikenal compiler pada baris pemanggilannya. - Elemen
@_merupakan alias dari argumen aslinya, sehingga menulis ke$_[0]mengubah variabel di pemanggil.
Referensi#
- perlsub — Perl documentation
— Aturan definisi subrutin, argumen
@_, dan bentuk pemanggilannya. - perlfunc — Perl documentation
— Daftar fungsi bawaan Perl, termasuk
shiftyang mengambil elemen dari depan argumen.