Setiap cabang case pada ekspresi match memuat satu pola, dan bentuk pola itulah yang menentukan nilai seperti apa yang dianggap cocok beserta ikatan yang dihasilkannya. Topik ini menguraikan pola literal, pola konstanta, pola variabel, pola tuple, pola barisan pada List, serta alternatif | yang menggabungkan beberapa pola dalam satu cabang. Cara compiler menentukan urutan pencocokan beserta akibat letak pola yang keliru dibahas pada bagian akhir. Setelah membacanya, setiap cabang case dapat ditulis dengan tahu persis pola mana yang digunakan.
Pola literal dan pola konstanta#
Pola literal adalah pola berupa nilai yang ditulis langsung, seperti 0, "S", atau true, dan pencocokannya membandingkan nilai yang diuji dengan nilai tertulis itu menggunakan kesetaraan. Pola konstanta berbentuk serupa, tetapi nilainya dirujuk melalui pengenal berhuruf kapital yang menunjuk ke val yang sudah ada, sehingga nilai yang sama dapat digunakan pada beberapa match tanpa ditulis ulang. Keduanya menghasilkan pencocokan yang paling langsung dan mudah dibaca. Program berikut menerjemahkan kode pintasan satu huruf ke dalam arti masing-masing.
// pola konstanta berhuruf besar merujuk val, pola variabel kecil mengikat
val KodeSelesai = "S"
val KodeBatal = "B"
@main def sandi(): Unit =
val masukan: List[String] = List("S", "B", "X")
for kode <- masukan do
val arti =
kode match
case KodeSelesai => "tugas selesai"
case KodeBatal => "tugas dibatalkan"
case lain => s"kode tidak dikenal: $lain"
println(s"$kode -> $arti")# menjalankan program literal.scala
scala-cli run literal.scalaS -> tugas selesai
B -> tugas dibatalkan
X -> kode tidak dikenal: XCabang pertama dan kedua menggunakan pola konstanta KodeSelesai dan KodeBatal yang menunjuk ke val berhuruf kapital pada berkas yang sama, sedangkan cabang ketiga menggunakan pola variabel lain untuk menampung kode yang tidak dikenal. Pencocokan pada dua cabang pertama berarti membandingkan kode dengan isi kedua konstanta itu, bukan mengikat ulang. Huruf kapital di sini bukan sekadar gaya penulisan, melainkan sinyal kepada compiler bahwa nilai pengenal tersebut harus dirujuk. Perlakuan berbeda terhadap huruf kecil dan huruf besar dibedah pada bagian berikutnya.
Pola variabel dan konstanta berhuruf kapital#
Compiler menentukan arti sebuah pengenal pada pola berdasarkan huruf pertamanya. Pengenal yang berawal huruf kecil selalu dibaca sebagai pola variabel, yaitu pola yang cocok dengan nilai apa pun sekaligus mengikatnya, meskipun ada val dengan nama yang sama di sekitarnya. Pengenal berawal huruf kapital dibaca sebagai rujukan konstanta, yaitu pencocokan terhadap nilai yang dirujuknya. Perbedaan ini menimbulkan jebakan ketika konstanta ditulis dengan huruf kecil, sebagaimana ditunjukkan program berikut yang menjalankan kedua bentuk tersebut berdampingan.
// pengenal kecil pada pola selalu mengikat nilai, bukan membandingkannya
val batas = 75
val Batas = 75
@main def perbandingan(): Unit =
val ujian: List[Int] = List(90, 75, 60)
println("pola variabel, pengenal kecil")
for n <- ujian do
val status =
n match
case batas => "batas"
case _ => "bukan batas"
println(s"$n -> $status")
println("pola konstanta, pengenal besar")
for n <- ujian do
val status =
n match
case Batas => "tepat di batas"
case _ => "bukan batas"
println(s"$n -> $status")# menjalankan program variabel.scala
scala-cli run variabel.scala[warn] ./variabel.scala:13:14
[warn] Unreachable case
[warn] case _ => "bukan batas"
[warn] ^
pola variabel, pengenal kecil
90 -> batas
75 -> batas
60 -> batas
pola konstanta, pengenal besar
90 -> bukan batas
75 -> tepat di batas
60 -> bukan batasKeluaran di atas diawali peringatan compiler sebelum keluaran program. Peringatan Unreachable case menunjuk ke case _ karena case batas cocok dengan nilai apa pun, sehingga cabang di bawahnya tidak mungkin terjangkau. Akibatnya terlihat pada blok pertama: ketiga nilai ujian tetap dicetak sebagai batas karena pola itu mengikat nilai, bukan membandingkan. Blok kedua menunjukkan perbaikannya, yaitu konstanta Batas berhuruf kapital sehingga hanya 75 yang dicetak sebagai tepat di batas.
Pola tuple#
Pola tuple mencocokkan pasangan atau gabungan nilai lain dengan membongkarnya menjadi pola-pola penyusunnya. Pola (0, 0) hanya cocok untuk pasangan yang kedua elemennya bernilai nol, sedangkan pola (x, 0) cocok untuk pasangan apa pun dengan elemen kedua bernilai nol dan mengikat elemen pertamanya ke x. Posisi penulisan menentukan makna: elemen pertama dipasangkan dengan pola pertama, elemen kedua dengan pola kedua. Program berikut mengelompokkan titik-titik pada bidang berdasarkan posisinya.
// pola tuple mendestrukturisasi pasangan menjadi pengenal-polanya
@main def posisi(): Unit =
val titik: List[(Int, Int)] = List((0, 0), (4, 0), (0, 3), (2, 5))
for t <- titik do
val keterangan =
t match
case (0, 0) => "titik asal"
case (x, 0) => s"di sumbu mendatar, x = $x"
case (0, y) => s"di sumbu tegak, y = $y"
case (x, y) => s"di kuadran dalam, ($x, $y)"
println(s"$t -> $keterangan")# menjalankan program tuple.scala
scala-cli run tuple.scala(0,0) -> titik asal
(4,0) -> di sumbu mendatar, x = 4
(0,3) -> di sumbu tegak, y = 3
(2,5) -> di kuadran dalam, (2, 5)Urutan cabang menunjukkan pola yang lebih spesifik diletakkan lebih dulu, sehingga (0, 0) ditangani sebelum (x, 0) dan (0, y). Elemen yang tidak diperlukan dapat diganti dengan tanda _, misalnya pola (0, _) yang hanya memeriksa elemen pertama. Pola tuple berlaku untuk tuple bertipe apa pun, dan jumlah pola penyusunnya wajib sama dengan jumlah elemen tuple yang diuji, sehingga pola (x, y) tidak akan pernah cocok dengan tuple tiga elemen. Tuple juga dapat bersarang, sehingga satu pola dapat membongkar pasangan yang isinya pasangan lagi.
case ((0, 0), (x, y)) => s"berangkat dari titik asal menuju ($x, $y)"Pola barisan pada List#
Pola barisan (sequence pattern) mencocokkan koleksi berurutan seperti List menurut jumlah dan susunan elemennya. Bentuk yang paling langsung adalah pola berupa daftar literal, misalnya List(satu) untuk daftar dengan tepat satu elemen dan List(a, b) untuk dua elemen, yang mengikat setiap elemen ke pengenalnya. Untuk daftar dengan panjang berapa pun, pola infiks kepala :: ekor memisahkan elemen pertama dari sisanya, dengan :: dibaca sebagai penghubung antara kepala dan ekor daftar. Pola Nil menangkap daftar kosong. Program berikut membedah empat daftar dengan panjang yang berbeda.
// pola barisan mencocokkan daftar menurut jumlah dan susunan elemennya
@main def bedahDaftar(): Unit =
val kumpulan: List[List[Int]] = List(List(), List(7), List(3, 9), List(1, 2, 5, 8))
for d <- kumpulan do
val keterangan =
d match
case Nil => "kosong"
case List(satu) => s"tepat satu elemen: $satu"
case List(a, b) => s"tepat dua elemen: $a dan $b"
case kepala :: ekor => s"berawalan $kepala, ekor ${ekor.length} elemen"
println(s"$d -> $keterangan")# menjalankan program barisan.scala
scala-cli run barisan.scalaList() -> kosong
List(7) -> tepat satu elemen: 7
List(3, 9) -> tepat dua elemen: 3 dan 9
List(1, 2, 5, 8) -> berawalan 1, ekor 3 elemenDaftar empat elemen tidak terjangkau oleh ketiga pola pertama karena jumlah elemennya berbeda, sehingga jatuh ke pola kepala :: ekor. Pola ini cocok dengan daftar apa pun yang tidak kosong, mengikat 1 ke kepala dan daftar List(2, 5, 8) ke ekor, dan itulah bentuk yang paling sering digunakan untuk menelusuri daftar secara rekursif. Ekor daftar juga dapat diikat sekaligus dengan pola barisan yang berakhir dengan tanda *.
case List(pertama, sisanya*) => s"pertama $pertama, sisanya ${sisanya.length} elemen"Pada pola di atas, sisanya* menampung nol elemen atau lebih setelah pertama, dan ikatannya bertipe Seq. Bentuk dengan tanda * inilah penulisan Scala 3 untuk pola barisan variabel yang pada Scala 2 ditulis sisanya @ _*.
Alternatif dan urutan pencocokan#
Tanda | menggabungkan beberapa pola menjadi satu alternatif, dan cabangnya cocok apabila salah satu pola dalam alternatif itu cocok. Program berikut memilah nama hari menggunakan dua alternatif, sehingga lima hari kerja tidak perlu ditulis sebagai lima cabang terpisah.
// tanda | menggabungkan beberapa pola pada satu case yang sama
@main def sifatHari(): Unit =
val urutanHari: List[String] = List("Sabtu", "Minggu", "Senin", "Jumat", "Kilat")
for hari <- urutanHari do
val sifat =
hari match
case "Sabtu" | "Minggu" => "akhir pekan"
case "Senin" | "Selasa" | "Rabu" | "Kamis" | "Jumat" => "hari kerja"
case lain => s"tidak dikenal: $lain"
println(s"$hari -> $sifat")# menjalankan program alternatif.scala
scala-cli run alternatif.scalaSabtu -> akhir pekan
Minggu -> akhir pekan
Senin -> hari kerja
Jumat -> hari kerja
Kilat -> tidak dikenal: KilatAlternatif tetap merupakan satu cabang, sehingga hasilnya hanya satu dan tidak boleh mengikat pengenal yang hanya ada pada sebagian polanya. Compiler menolak pola seperti case x | "Kilat" dengan pesan error Illegal variable x in pattern alternative, karena x hanya terikat pada satu sisi alternatif.
Pencocokan tetap berjalan dari atas ke bawah, sehingga cabang pertama yang cocok menang dan cabang-cabang di bawahnya tidak diuji lagi. Aturan praktisnya adalah menempatkan pola paling spesifik di atas dan pola paling umum di bawah, karena pola variabel yang diletakkan lebih dulu menyerap semua nilai dan membuat cabang sesudahnya sia-sia.
val keterangan =
n match
case lain => "selalu cocok"
case 0 => "tidak pernah terjangkau"Compiler menandai case 0 dengan peringatan Unreachable case, karena case lain di atasnya sudah menangkap nilai apa pun. Aturan letak yang sama berlaku untuk pola lain: pola yang lebih sempit, seperti List(a, b), ditulis sebelum pola yang lebih luas, seperti kepala :: ekor. Pola yang mendestrukturisasi objek case class menggunakan mekanisme pencocokan yang sama dan diuraikan pada topik destrukturisasi data.
Ringkasan#
- Pola literal dan pola konstanta membandingkan nilai secara langsung, dengan konstanta ditulis berhuruf kapital sebagai rujukan
val. - Pola variabel berhuruf kecil cocok dengan nilai apa pun dan mengikatnya, sehingga konstanta yang ditulis dengan huruf kecil membuat pencocokan gagal tanpa terlihat.
- Pola tuple membongkar pasangan menurut posisi, pola barisan mencocokkan
Listmenurut jumlah dan susunannya dengan::dan*, sedangkan|menggabungkan beberapa pola dalam satu cabang. - Pencocokan berjalan dari atas ke bawah dan berhenti pada cabang pertama yang cocok, sehingga pola spesifik ditulis lebih dulu dan pola umum ditutup di bagian bawah.
Referensi#
- Pattern Matching — Scala 3 Book — Bentuk-bentuk pola pada match expression, termasuk pola literal, variabel, dan koleksi.
- Pattern Matching — Tour of Scala — Ringkasan pola literal, variabel, tuple, dan barisan pada tur resmi bahasa Scala.
- List — Scala Standard Library
— Dokumentasi API
Listyang menjadi sasaran pola::,Nil, dan pola barisan lainnya.