Guard dan klausul when

Klausul when untuk menyempurnakan pola individual dengan syarat yang lebih halus.

Daftar isi

Topik ini membahas klausul when, yaitu guard atau syarat tambahan yang menempel pada satu arm match untuk menyaring nilainya secara lebih halus daripada pola biasa. Pembahasan mencakup penulisan guard pada pola literal dan pada pola yang sudah membongkar data, perilaku pencocokan apabila syarat tidak terpenuhi, serta akibat guard terhadap pemeriksaan kelengkapan pola.

Guard pada satu arm#

Klausul when ditulis di antara pola dan panah arm dengan bentuk | pola when syarat -> ekspresi. Arm hanya digunakan apabila polanya cocok dan syaratnya bernilai true. Apabila syarat bernilai false, pencocokan berlanjut ke arm di bawahnya, sehingga guard bekerja seperti saringan yang meneruskan nilai ke arm berikutnya. Nama yang diikat oleh pola dapat dirujuk langsung di dalam syarat.

Berkas berikut mengklasifikasikan nilai ujian menjadi predikat A sampai E. Rentang nilai tidak dapat ditulis sebagai pola literal, tetapi tiap arm mengikat nilai ke n dan mengujinya dengan when. Nilai 72 melewati arm pertama karena 72 >= 85 bernilai false, lalu tertangkap di arm kedua karena 72 >= 70 bernilai true. Arm wildcard di paling bawah menampung nilai yang gagal pada seluruh syarat.

fsharp klasifikasi-nilai-when.fsx
// Klausul when menyaring pola dengan syarat tambahan
let predikat nilai =
    match nilai with
    | n when n >= 85 -> "A"
    | n when n >= 70 -> "B"
    | n when n >= 55 -> "C"
    | n when n >= 40 -> "D"
    | _ -> "E"

for nilai in [95; 72; 60; 45; 30] do
    printfn "nilai %d mendapat predikat %s" nilai (predikat nilai)
text Keluaran
nilai 95 mendapat predikat A
nilai 72 mendapat predikat B
nilai 60 mendapat predikat C
nilai 45 mendapat predikat D
nilai 30 mendapat predikat E

Tanpa when, syarat yang sama harus ditulis sebagai if di dalam ekspresi arm. Bentuk tersebut membuat arm pertama menangkap semua nilai, sehingga arm sesudahnya tidak pernah tercapai dan percabangannya menumpuk di dalam satu ekspresi. Klausul when memecah persoalan itu, yaitu tiap syarat menjadi arm tersendiri yang diperiksa berurutan, sehingga urutan pengujian terbaca langsung dari atas ke bawah.

Guard pada pola yang membongkar data#

Guard dapat dipasangkan dengan pola apa pun, termasuk pola yang membongkar tuple. Syaratnya juga boleh menggabungkan beberapa pengujian dengan operator boolean && dan ||. Kombinasi pola dan guard menghasilkan penyaringan yang presisi, yaitu bentuk data diperiksa oleh pola dan hubungan antarbagiannya diperiksa oleh guard.

Berkas berikut menguji pasangan angka dengan tiga arm. Dua arm pertama menggunakan pola (a, b) yang membongkar pasangan sekaligus guard yang merujuk ke a dan b, sedangkan arm terakhir berupa arm pengikat biasa. Hasilnya menunjukkan bahwa (5, 5) tertangkap syarat kesamaan, (2, 8) tertangkap syarat jumlah sepuluh, dan sisanya jatuh ke arm penutup.

fsharp pasangan-angka.fsx
// Guard menyaring pasangan angka yang sudah dibongkar dari tuple
let kategorikan pasangan =
    match pasangan with
    | (a, b) when a = b -> sprintf "kedua angka sama, yaitu %d" a
    | (a, b) when a + b = 10 -> sprintf "jumlahnya sepuluh, yaitu %d dan %d" a b
    | (a, b) -> sprintf "pasangan biasa, yaitu %d dan %d" a b

printfn "%s" (kategorikan (5, 5))
printfn "%s" (kategorikan (2, 8))
printfn "%s" (kategorikan (3, 4))
text Keluaran
kedua angka sama, yaitu 5
jumlahnya sepuluh, yaitu 2 dan 8
pasangan biasa, yaitu 3 dan 4

when dan kelengkapan pola#

Compiler tidak membaca isi syarat when ketika memeriksa kelengkapan pola. Bagi compiler, arm berguard tidak menutup kasus mana pun, sehingga match yang seluruh armnya berguard tetap dianggap tidak lengkap meskipun syaratnya sudah menutup semua kemungkinan. Skrip guard-peringatan.fsx berikut memperlihatkan kondisi itu. Rentang syaratnya sebenarnya mencakup seluruh bilangan nonnegatif, tetapi peringatan FS0025 tetap muncul, dan kali ini tanpa contoh nilai karena compiler tidak dapat menunjuk nilai yang pasti terlewat.

bash
# tampilkan isi berkas skripnya
cat guard-peringatan.fsx
# jalankan skripnya dari folder topik
dotnet fsi guard-peringatan.fsx
text Keluaran
// Semua arm memakai guard, tanpa pola penutup
let predikat nilai =
    match nilai with
    | n when n >= 85 -> "A"
    | n when n >= 70 -> "B"
    | n when n >= 55 -> "C"
    | n when n >= 40 -> "D"
    | n when n >= 0 -> "E"

/home/kal/my-project/content/fsharp/pattern-matching/guard-klausul-when/guard-peringatan.fsx(3,11): warning FS0025: Incomplete pattern matches on this expression.

Perbaikannya konsisten dengan pola penutup pada bagian sebelumnya, yaitu menambahkan arm wildcard di paling bawah seperti yang digunakan klasifikasi-nilai-when.fsx. Arm penutup tersebut hanya tercapai apabila seluruh guard di atasnya gagal, sehingga kelengkapan tercukupi tanpa mengurangi ketepatan arm berguard di atasnya.

Urutan pengecekan guard juga berjalan dari atas ke bawah seperti pola biasa. Nilai 45 pada keluaran klasifikasi di bagian pertama melewati tiga syarat sebelum tertangkap pada arm dengan syarat 45 >= 40. Nilai 30 jatuh ke arm penutup karena seluruh syarat di atasnya gagal.

Memilih antara guard dan pola literal#

Guard dan pola literal memiliki wilayah kerja yang berbeda dan saling melengkapi. Pola literal mencocokkan nilai yang sudah diketahui dan ikut diperhitungkan compiler saat memeriksa kelengkapan, sedangkan guard menguji rentang atau hubungan yang tidak dapat ditulis sebagai literal. Tabel berikut merangkum perbedaannya.

AspekPola literalGuard when
Bentuk armpola ->pola when syarat ->
Yang dicocokkannilai tetap yang ditulis langsunghasil ekspresi boolean
Diperhitungkan untuk kelengkapanya, literal ikut dihitungtidak, arm berguard dianggap tidak menutup
Contoh penggunaankode hari, kode statusrentang nilai, kesamaan antarbagian data

Sebagai pedoman pemilihan, gunakan pola literal apabila himpunan nilainya kecil dan diketahui sejak awal, lalu gunakan guard apabila pengujinya bersyarat. Keduanya boleh bercampur dalam satu match, sebagaimana klasifikasi-nilai-when.fsx yang menggunakan arm pengikat berguard di atas dan wildcard di bawah. Campuran semacam ini lazim pada pencocokan data yang sebagian kasusnya berupa nilai pasti dan sebagiannya berupa rentang.

Susunan yang lazim adalah tiga lapis, yaitu arm literal untuk nilai-nilai khusus, arm berguard untuk kelompok yang tersisa, dan arm penutup di paling bawah. Susunan ini membuat maksud tiap arm terbaca tanpa membaca syaratnya, sekaligus menjaga match tetap lengkap di mata compiler. Berkas klasifikasi-nilai-when.fsx mengikuti bentuk yang sama dengan empat arm berguard dan satu arm penutup.

Ringkasan#

  • Klausul when menyaring satu arm dengan syarat boolean, dan arm yang syaratnya gagal meneruskan pencocokan ke arm berikutnya.
  • Guard dapat menempel pada pola apa pun, termasuk pola tuple yang sudah membongkar datanya, dan syaratnya boleh menggabungkan beberapa pengujian.
  • Compiler menganggap arm berguard tidak menutup kasus, sehingga match berguard tetap membutuhkan arm penutup.
  • Pola literal dipilih untuk nilai pasti yang ikut diperiksa kelengkapannya, sedangkan guard dipilih untuk rentang dan hubungan antarbagian data.

Referensi#