Pernyataan case memilih satu cabang eksekusi dengan mencocokkan sebuah nilai terhadap daftar pola secara berurutan. Crystal menyediakan dua bentuknya, yaitu case ... when untuk pencocokan nilai bebas dan case ... in untuk pencocokan pola (pattern matching) yang kelengkapannya diperiksa compiler. Topik ini membahas kedua bentuk tersebut, pola yang didukung setiap bentuk, pengikatan elemen tuple dengan destructuring, serta perbedaan perilaku keduanya. Semua contoh dijalankan dengan Crystal 1.22.1.
Pencabangan case dengan when#
Cabang when menerima ekspresi apa pun sebagai polanya. Subjek case dievaluasi sekali, lalu dicocokkan ke setiap cabang dari atas ke bawah, dan hanya cabang pertama yang cocok yang dijalankan. Rentang dapat digunakan langsung sebagai pola, sementara beberapa nilai yang diperlakukan setara ditulis dipisah koma pada satu cabang. Cabang else menampung nilai yang tidak cocok dengan cabang mana pun.
kode = 7
kategori = case kode
when 1..3
"rendah"
when 4..9
"sedang"
else
"tinggi"
end
puts kategori
warna = "hijau"
grup = case warna
when "merah", "kuning"
"hangat"
when "hijau", "biru"
"dingin"
else
"lain"
end
puts grupsedang
dinginPada pencocokan pertama, nilai 7 tidak termasuk rentang 1..3 tetapi termasuk 4..9, sehingga kategori bernilai “sedang”. Pada pencocokan kedua, string “hijau” cocok dengan salah satu nilai pada cabang keduanya. Urutan cabang menentukan hasil ketika satu nilai masuk dalam beberapa pola, sehingga pola yang paling spesifik ditulis lebih dahulu.
Pencocokan tipe dengan when#
Objek tipe juga dapat digunakan sebagai pola when. Pencocokan tipe lazim diterapkan terhadap tipe union, yaitu gabungan beberapa kemungkinan tipe pada satu variabel. Di dalam cabang yang cocok, compiler menyempitkan tipe subjeknya, sehingga metode dan operator milik tipe tersebut boleh dipanggil tanpa konversi.
def deskripsikan(nilai : Int32 | String) : String
case nilai
when Int32
"bilangan bulat #{nilai + 1}"
when String
"teks #{nilai.upcase}"
else
"lain"
end
end
puts deskripsikan(7)
puts deskripsikan("halo")bilangan bulat 8
teks HALOPada cabang when Int32, subjek diketahui bertipe Int32 sehingga nilai + 1 dapat dihitung. Pada cabang when String, metode upcase milik String dapat dipanggil langsung. Pemanggilan metode yang tidak dimiliki tipe cabangnya akan ditolak compiler, sehingga penyempitan tipe ini menjadi jaminan keamanan, bukan sekadar penanda. Fungsi dengan tanda tangan Int32 | String menjadi contoh yang tepat karena setiap cabangnya mengurus satu kemungkinan tipe secara terpisah.
Pernyataan case tanpa subjek#
case dapat ditulis tanpa subjek. Bentuk ini mengevaluasi setiap kondisi pada when sebagai ekspresi boolean dan menjalankan cabang pertama yang bernilai benar. Pola seperti ini praktis ketika keputusan bergantung pada perbandingan yang berbeda-beda dan tidak ada satu nilai tunggal untuk dicocokkan. Kondisi yang melibatkan beberapa variabel sekaligus juga dapat ditulis pada bentuk ini.
suhu = 31
pesan = case
when suhu < 20
"dingin"
when suhu < 35
"sedang"
else
"panas"
end
puts pesansedangSuhu 31 gagal pada kondisi suhu < 20 dan lulus pada suhu < 35, sehingga pesan yang dihasilkan adalah “sedang”. Cabang else tetap berlaku sebagai penampung kondisi yang tidak terpenuhi. Bentuk tanpa subjek ini setara dengan rantai if ... elsif ... else, tetapi menonjolkan daftar kondisinya secara berurutan. Pembagian kategori terhadap rentang nilai seperti di atas adalah penggunaan bentuk ini yang lazim.
Pencocokan pola dengan in#
Bentuk case ... in adalah pencocokan pola dengan ragam pola yang lebih terbatas tetapi kelengkapannya diperiksa compiler. Pola yang sah berupa konstanta, tipe generik seperti Array(Int32), nilai boolean true atau false, literal nil, dan question method, yaitu pemanggilan metode predikat berawalan titik seperti .merah?. Bentuk ini dirancang agar setiap kemungkinan nilai subjek memiliki cabangnya. Contoh berikut mencakup kedua tipe pada union-nya dengan pola tipe.
def deskripsikan(nilai : Int32 | String) : String
case nilai
in Int32
"bilangan bulat #{nilai + 1}"
in String
"teks #{nilai.upcase}"
end
end
puts deskripsikan(7)
puts deskripsikan("halo")bilangan bulat 8
teks HALOSama seperti when, tipe subjek di dalam cabang ikut disempitkan sehingga nilai + 1 dan upcase sah dipanggil. Perbedaannya ada pada pemeriksaan yang dilakukan compiler terhadap cabang-cabangnya, sebagaimana ditunjukkan bagian berikutnya.
Pemeriksaan kelengkapan oleh compiler#
Cabang yang hilang dilaporkan compiler apabila tipe yang bersangkutan benar-benar mengalir ke subjek. Program berikut sama dengan sebelumnya, tetapi cabang in String dihapus dan fungsinya dipanggil dengan String.
def deskripsikan(nilai : Int32 | String) : String
case nilai
in Int32
"bilangan bulat #{nilai + 1}"
end
end
puts deskripsikan("halo")Showing last frame. Use --error-trace for full trace.
In error_tidak_lengkap.cr:2:3
2 | case nilai
^
Error: case is not exhaustive.
Missing types:
- StringPesan case is not exhaustive menyatakan tipe yang belum tercakup, yaitu String. Pemeriksaan ini mengikuti tipe yang benar-benar digunakan, karena fungsi yang hanya pernah dipanggil dengan Int32 tidak menuntut cabang String. Untuk enum, pemeriksaannya diterapkan sepenuhnya tanpa melihat pemanggilannya. Program berikut mencakup kedua anggota enum dengan pola question method.
enum Warna
Merah
Hijau
end
def nama(w : Warna) : String
case w
in .merah?
"merah"
in .hijau?
"hijau"
end
end
puts nama(Warna::Merah)
puts nama(Warna::Hijau)merah
hijauApabila salah satu anggota enum tidak tercakup, compiler menolak programnya dengan pesan case is not exhaustive for enum Warna. yang lalu mencantumkan anggota yang hilang, yaitu Hijau. Pernyataan else juga tidak diperbolehkan pada bentuk in dan ditolak dengan pesan exhaustive case (case ... in) doesn't allow an 'else'. Kedua pesan ini memperlihatkan sifat kelengkapan yang menjadi ciri bentuk in.
Destructuring tuple dan pola tipe#
Elemen tuple dapat diikat ke variabel dengan destructuring, yaitu penulisan beberapa variabel sekaligus pada sisi kiri penugasan. Pencocokan struktur tuple juga dapat dilakukan pada when dengan pola tuple literal. Keduanya digunakan bersama pada program berikut.
pasangan = {1, "satu"}
a, b = pasangan
puts a + 1
puts b
case pasangan
when {1, "satu"}
puts "pasangan cocok"
end2
satu
pasangan cocokPengikatan a, b = pasangan menarik elemen pertama ke a dan elemen kedua ke b, sedangkan pola {1, "satu"} pada when mencocokkan struktur dan nilai tuple sekaligus. Bentuk pengikatan nama ini tidak tersedia pada in. Pola yang berisi variabel baru ditolak compiler dengan pesan yang menjelaskan bentuk pola yang sah.
pasangan = {1, "satu"}
case pasangan
in {a, b}
puts a + 1
puts b
endIn error_pola_tuple.cr:3:4
3 | in {a, b}
^
Error: expression of exhaustive case (case ... in) must be a constant (like `IO::Memory`), a generic (like `Array(Int32)`), a bool literal (true or false), a nil literal (nil) or a question method (like `.red?`)Struktur tuple tetap dapat dicocokkan pada in melalui pola tipe generik, yaitu penulisan tipe tuple secara lengkap. Pola Tuple(Int32, String) mencocokkan tuple dua elemen dengan tipe yang sama persis, dan pola serupa tersedia untuk NamedTuple melalui NamedTuple(nama: String, umur: Int32).
pasangan = {1, "satu"}
case pasangan
in Tuple(Int32, String)
puts "tuple Int32 dan String"
endtuple Int32 dan StringPerbedaan when dan in#
Perbedaan kedua bentuk terletak pada pemeriksaan compiler dan ragam pola yang diterima. Tabel berikut merangkumnya.
| Aspek | when | in |
|---|---|---|
| Pemeriksaan kelengkapan cabang | Tidak dilakukan | Dilakukan menurut tipe yang mengalir, anggota enum, dan Bool |
Cabang else | Tersedia | Ditolak compiler |
| Ragam pola | Ekspresi apa pun, termasuk rentang, beberapa nilai, dan tuple literal | Konstanta, tipe generik, true, false, nil, dan question method |
| Penyempitan tipe di dalam cabang | Dilakukan | Dilakukan |
Pada praktiknya, when digunakan untuk pencocokan nilai yang bebas, sedangkan in digunakan ketika setiap kemungkinan ingin dijamin tercakup. Fungsi berikut memperlihatkan case ... in yang mencakup tiga tipe union sekaligus dengan tiga pola tipe.
def deskripsikan(nilai : Int32 | String | Array(Int32)) : String
case nilai
in Int32
"bilangan bulat"
in String
"teks"
in Array(Int32)
"larik bilangan bulat"
end
end
puts deskripsikan(7)
puts deskripsikan("halo")
puts deskripsikan([1, 2, 3])bilangan bulat
teks
larik bilangan bulatTiga pola tipe menutup tiga kemungkinan tipe pada tanda tangan fungsi. Pola ketiga sekaligus menunjukkan bahwa pola tipe generik menerima parameter tipenya juga, sehingga Array(Int32) dapat dipisahkan dari array dengan tipe elemen lain. Apabila satu pola dihapus dan fungsi dipanggil dengan tipe tersebut, compiler menolak programnya seperti pada contoh bagian Pemeriksaan kelengkapan oleh compiler. Struktur ini membuat pergantian tanda tangan fungsi langsung menuntut pembaruan daftar cabangnya.
Ringkasan#
case ... whenmencocokkan nilai, rentang, beberapa nilai sekaligus, tipe, dan tuple literal, tanpa pemeriksaan kelengkapan dan denganelsetersedia.case ... inmenerima pola terbatas berupa konstanta, tipe generik,true,false,nil, dan question method, serta menolak cabangelse.- Compiler menuntut cabang untuk tipe yang mengalir ke subjek
in, dan untuk enum menuntut semua anggota tercakup. - Elemen tuple diikat dengan destructuring assignment, sedangkan struktur tuple pada
indicocokkan dengan pola tipe generik sepertiTuple(Int32, String).
Referensi#
- Dokumentasi bahasa Crystal 1.22 — crystal-lang.org — Rujukan sintaks dan semantik bahasa, termasuk bab pernyataan case.
- Referensi API Crystal 1.22.1 — crystal-lang.org
— Dokumentasi API standard library, termasuk
Tuple,NamedTuple, danEnum.