Pattern matching (pencocokan pola) memperluas pencocokan nilai menjadi pencocokan pola terhadap struktur data. Konstruksi case/in yang diperkenalkan pada Ruby 3 dapat membongkar isi array dan hash ke dalam variabel, menyaring cabang dengan syarat tambahan, dan menguji kesetaraan dengan nilai di luar pola. Topik ini membahas pola dasar, destructuring array dan hash, guard, dan pin, lalu menutupnya dengan penggolongan entri antrean yang memadukan seluruh pola tersebut.
Pencocokan pola dengan case/in#
Konstruksi case/in berbentuk seperti case/when, tetapi cabang in memuat pola yang diuji terhadap struktur nilai, bukan sekadar pembanding tunggal. Pola paling sederhana berupa literal atau kelas, sedangkan pola array dan hash memeriksa isi sekaligus mengikatnya ke variabel baru. Contoh berikut menguji sebuah array terhadap pola [Integer, String] dan terhadap pola String.
ukuran = [27, "nyaman"]
case ukuran
in [Integer, String]
puts "Pengukuran lengkap: #{ukuran.join(" ")}"
in String
puts "Hanya label: #{ukuran}"
endPengukuran lengkap: 27 nyamanPotongan berikut membalik urutan jenis pada polanya, sehingga tidak ada cabang yang cocok dengan nilainya.
# tidak-dijalankan
case [27, "nyaman"]
in [String, Integer]
puts "cocok"
endsalah-pola.rb:2:in '<main>': [27, "nyaman"]: String === 27 does not return true (NoMatchingPatternError)Pesan error menunjukkan nilai yang gagal dicocokkan beserta pembanding yang gagal lebih dulu, dan nama berkasnya menyesuaikan dengan berkas yang dijalankan. Cabang else mencegah error ini dengan menampung nilai yang tidak cocok dengan pola mana pun. Program berikut menggunakan pola lengkap tanpa else karena keempat bentuk datanya sudah tercakup.
# Penggolongan entri antrean tiket dengan pattern matching.
entri = [
{nomor: 1, nama: "Ali"},
{nomor: 2, nama: "Budi", prioritas: true},
[3, "Citra"],
"selesai"
]
entri.each do |e|
pesan =
case e
in {nomor: Integer => nomor, prioritas: true}
"Nomor #{nomor} dilayani lebih dulu"
in {nomor: Integer => nomor}
"Nomor #{nomor} menunggu"
in [Integer => nomor, String => nama]
"Nomor #{nomor} atas nama #{nama} menunggu"
in "selesai"
"Antrean ditutup"
end
puts pesan
endNomor 1 menunggu
Nomor 2 dilayani lebih dulu
Nomor 3 atas nama Citra menunggu
Antrean ditutupUrutan pola menentukan hasil sebagaimana pada case/when. Entri pertama tidak membawa kunci prioritas, sehingga jatuh ke pola hash sederhana, sedangkan entri kedua ditangkap pola prioritas yang ditulis lebih dulu. Dua baris terakhir memperlihatkan pola array dan pola literal string yang diuji dengan kesetaraan nilai.
Destructuring array#
Destructuring, yaitu pembongkaran struktur data ke dalam beberapa variabel sekaligus, dilakukan oleh pola array dengan menuliskan bentuk array yang diharapkan di dalam in. Setiap posisi pola dapat berupa kelas, literal, atau nama variabel baru yang langsung terikat nilainya. Tanda bintang menampung sisa elemen ke dalam satu array, sehingga panjang data yang berubah-ubah tetap tertangani.
barisan = [1, 2, 3, 4, 5]
case barisan
in [pertama, *tengah, terakhir]
puts "Pertama #{pertama}, terakhir #{terakhir}, tengah #{tengah.length} elemen"
endPertama 1, terakhir 5, tengah 3 elemenPola [pertama, *tengah, terakhir] mengikat elemen pertama dan terakhir ke variabelnya, lalu menampung tiga elemen sisanya ke variabel tengah. Tanda bintang hanya boleh muncul sekali dalam satu pola dan boleh tanpa nama, ditulis * apabila sisanya tidak diperlukan. Pola kosong [] hanya cocok untuk array tanpa isi, dan pola dapat disarangkan seperti [Integer, [String]] untuk data bersarang.
Destructuring hash#
Pola hash ditulis dengan sintaks pembuatan hash dan dicocokkan menurut kuncinya. Hanya kunci yang disebutkan yang diwajibkan ada, sedangkan kunci lain dibiarkan lewat, sehingga pola tetap cocok ketika datanya membawa informasi tambahan. Nilai di belakang kunci berupa pola apa pun, termasuk pola kelas yang diikuti => untuk mengikat nilainya ke variabel.
tiket = {nomor: 12, tujuan: "Loket B", nama: "Ali"}
case tiket
in {nomor: Integer => nomor, tujuan: String => tujuan, **}
puts "Tiket #{nomor} menuju #{tujuan}"
endTiket 12 menuju Loket BBagian Integer => nomor melakukan dua hal sekaligus, yaitu memeriksa bahwa nilai kunci nomor berupa Integer dan mengikatnya ke variabel nomor. Tanda ** di ujung pola menyatakan sisa kunci boleh ada dengan bentuk apa pun. Apabila sisa kunci justru harus ditolak, tuliskan **nil sebagai penggantinya, sehingga hash yang membawa kunci tak dikenal tidak cocok dengan pola tersebut.
Guard dan pin#
Syarat tambahan setelah pola, yang disebut guard, mempersempit cabang tanpa menambah tingkat sarang. Guard ditulis sebagai if di akhir pola dan dinilai setelah pola terikat, sehingga variabel hasil ikatan dapat digunakan di dalamnya. Cabang yang polanya cocok tetapi guardnya palsu dilompati, dan pencocokan berlanjut ke pola berikutnya.
pengukuran = [38, "tinggi"]
case pengukuran
in [Integer => suhu, String => label] if suhu >= 35
puts "Peringatan: suhu #{suhu} derajat #{label}"
in [Integer => suhu, String => label]
puts "Suhu normal: #{suhu} derajat #{label}"
endPeringatan: suhu 38 derajat tinggiPola pin menggunakan tanda sirkumfleks ^ untuk menguji kesetaraan dengan nilai yang sudah ada di luar pola. Tanpa pin, nama variabel di dalam pola selalu membuat ikatan baru, sehingga target akan menimpa nilai lamanya. Pin mencegah hal itu dengan membandingkan nilai pola dengan variabel luar.
target = 27
case [27, 30]
in [^target, Integer => berikutnya]
puts "Pengukuran pertama sesuai target, berikutnya #{berikutnya}"
endPengukuran pertama sesuai target, berikutnya 30Pada contoh di atas, ^target menuntut elemen pertama sama dengan 27, sedangkan berikutnya tetap menjadi ikatan baru. Pin juga dapat membungkus ekspresi dengan tanda kurung, misalnya ^(suhu + 1), sehingga ambang yang dihitung saat program berjalan tetap dapat diuji. Dengan pin, guard, dan destructuring yang dipadukan, satu cabang in dapat menyatakan bentuk, jenis, dan syarat nilai sekaligus.
Ringkasan#
case/inmencocokkan pola terhadap struktur data dan mengikat bagian yang cocok ke variabel baru, denganelsesebagai penangkap nilai tak dikenal.- Pola array membongkar elemen menurut posisi dengan tanda bintang untuk sisa elemen, sedangkan pola hash membongkar menurut kunci dan membiarkan kunci lain lewat.
- Guard
ifmenyaring cabang setelah pola terikat, dan pin^menguji kesetaraan dengan nilai variabel di luar pola. - Tanpa pola yang cocok dan tanpa
else,case/inmemunculkanNoMatchingPatternError.
Referensi#
- Pattern matching — Ruby — Dokumentasi resmi pola array dan hash, guard, pin, serta perilaku errornya.