Menulis metode penerima block

Mekanika yield di dalam metode sendiri dan pemeriksaan block_given?.

Daftar isi

Metode penerima blok (block) adalah metode yang menjalankan blok yang menyertai pemanggilannya melalui kata kunci yield, sehingga sebagian perilakunya ditentukan oleh blok pada setiap pemanggilan. Metode each, map, dan times pada pustaka standar bekerja dengan mekanika yang sama. Topik ini membahas penulisan metode semacam itu, yaitu penempatan yield, penyampaian nilai kepada blok, pemanfaatan nilai kembalian blok, serta pemeriksaan block_given? agar metode tetap dapat dipanggil tanpa blok. Setelah membacanya, metode dengan perilaku yang dapat disesuaikan oleh pemanggilnya dapat dirancang dengan pola yang baku.

yield di dalam metode yang ditulis sendiri#

Metode penerima blok ditulis sebagaimana metode biasa, tetapi badannya memuat satu atau lebih pernyataan yield. Posisi yield menentukan titik di mana blok berjalan, dan yield yang berada di dalam perulangan menjalankan blok pada setiap iterasinya. Contoh berikut menulis iterator sederhana bernama ulangi, yaitu metode yang mengurus penghitungan langkah, sedangkan blok menyediakan pekerjaan pada setiap langkahnya. Pembagian tugas seperti ini menjadi pola dasar penulisan iterator sendiri.

ruby ulangi.rb
def ulangi(jumlah_langkah)
  nomor = 1
  while nomor <= jumlah_langkah
    yield nomor
    nomor += 1
  end
end

ulangi(3) { |n| puts "Tiket nomor #{n} dipanggil" }
text Keluaran
Tiket nomor 1 dipanggil
Tiket nomor 2 dipanggil
Tiket nomor 3 dipanggil

Pemanggilan ulangi(3) menyertakan blok yang menerima satu parameter. Badan metode menjalankan perulangan while tiga kali, dan setiap iterasi mengeksekusi yield dengan nomor langkah terkini sebagai nilainya. Pembagian tugasnya jelas, yaitu metode mengurus pola berulang dan pencatatan langkah, sedangkan blok mengisi pekerjaan tiap langkah. Metode each pada array bekerja dengan pola yang persis sama, yaitu perulangan internal yang memanggil yield sekali per elemen. Nilai kembalian blok pada each diabaikan, sedangkan map mengumpulkannya menjadi array baru.

Beberapa nilai dan nilai kembalian blok#

Satu pemanggilan yield dapat menyampaikan beberapa nilai sekaligus, dan nilai kembalian blok kembali ke metode untuk digunakan pada proses berikutnya. Dua arah ini bersama-sama membentuk kontrak lengkap antara metode dan blok, yaitu data yang dikirim masuk dan keputusan yang dikirim keluar. Contoh berikut menghitung jumlah siswa yang lulus dengan batas kelulusan yang ditentukan blok.

ruby hitung-lulus.rb
def hitung_lulus
  daftar = [["Ali", 78], ["Budi", 65], ["Citra", 90], ["Dewi", 55]]
  lulus = 0
  i = 0

  while i < daftar.length
    if yield(daftar[i][0], daftar[i][1])
      lulus += 1
    end
    i += 1
  end

  lulus
end

jumlah = hitung_lulus do |nama, nilai|
  nilai >= 70
end

puts "#{jumlah} siswa lulus"
text Keluaran
2 siswa lulus

Metode hitung_lulus menyampaikan dua nilai pada setiap yield, yaitu nama dan nilai ujian, sehingga blok menerimanya melalui parameter |nama, nilai|. Keputusan batas kelulusan tidak tertulis di dalam metode, melainkan berada di blok yang mengembalikan benar atau salah. Pernyataan if menggunakan nilai kembalian blok untuk menambah penghitung, dan metode mengembalikan jumlahnya setelah seluruh daftar selesai diproses. Apabila batas kelulusan berubah menjadi 60, cukup blok pada pemanggilan metode yang ditulis ulang menjadi nilai >= 60, tanpa mengubah metodenya.

block_given?#

Metode block_given? mengembalikan true apabila pemanggilan menyertakan blok dan false apabila tidak, sehingga satu metode dapat menyediakan dua jalur perilaku. Tanpa pemeriksaan ini, yield di dalam metode yang dipanggil tanpa blok menimbulkan LocalJumpError. Pemeriksaan ini dapat digunakan di dalam metode mana pun tanpa persiapan apa pun karena block_given? merupakan metode modul Kernel yang tersedia pada setiap objek Ruby. Pola blok opsional seperti ini lazim pada metode pustaka standar, misalnya sort pada array yang mengurutkan secara alami tanpa blok dan mengikuti perbandingan blok apabila blok disertakan.

ruby block-given.rb
def laporkan(judul)
  if block_given?
    puts "== #{judul} =="
    yield
  else
    puts "#{judul}: (tanpa rincian)"
  end
end

laporkan("Statistik kelas")
laporkan("Statistik kelas") do
  puts "Rata-rata nilai: 82"
  puts "Nilai tertinggi: 90"
end
text Keluaran
Statistik kelas: (tanpa rincian)
== Statistik kelas ==
Rata-rata nilai: 82
Nilai tertinggi: 90

Pemanggilan pertama berjalan tanpa blok, sehingga block_given? bernilai false dan cabang else mencetak baris ringkas. Pemanggilan kedua menyertakan blok, sehingga metode mencetak judul berbingkai terlebih dahulu, lalu yield menjalankan isi blok sebagai rinciannya. Rancangan semacam ini menjadikan perilaku default tetap berguna, sedangkan blok menambah kedalaman laporan apabila pemanggil membutuhkannya.

Kesalahan umum#

Kesalahan yang sering muncul adalah keliru menempatkan blok do ... end pada rantai pemanggilan. Blok do ... end menempel pada pemanggilan metode paling kiri pada rantai pemanggilan, bukan selalu pada metode yang dimaksud penulisnya. Kurung kurawal berbeda karena selalu menempel pada pemanggilan yang paling dekat dengannya. Kasus berikut diperagakan dengan metode bantu yang melaporkan apakah blok benar-benar diterimanya.

ruby do-end-pada-puts.rb
# Bentuk do...end yang menempel pada pemanggilan terdekat
def proses(daftar)
  if block_given?
    daftar.map { |n| yield n }
  else
    "block tidak diterima"
  end
end

puts proses([1, 2, 3]) do |n|
  n * 2
end

puts proses([1, 2, 3]) { |n| n * 2 }
text Keluaran
block tidak diterima
2
4
6

Pada pemanggilan pertama, blok do ... end menempel pada puts karena do ... end menempel pada pemanggilan paling kiri pada rantai tanpa tanda kurung, yaitu puts, dan bukan pada proses yang posisinya lebih dekat. Metode proses kemudian menerima pemanggilan tanpa blok, sehingga block_given? bernilai false dan metode mengembalikan string block tidak diterima. Pemanggilan kedua menggunakan kurung kurawal yang menempel pada proses, sehingga blok diterima dan hasil penggandaannya tercetak sebagai 2, 4, 6. Di dalam badan map pada pemanggilan kedua, yield tetap menunjuk blok milik metode proses, bukan blok milik map.

Ringkasan#

  • Metode penerima blok menempatkan yield pada posisi di mana blok harus berjalan, dan perulangan dapat memanggil yield berulang kali.
  • Satu yield dapat menyampaikan beberapa nilai, dan nilai kembalian blok kembali ke metode untuk digunakan prosesnya.
  • block_given? menguji keberadaan blok sehingga metode dapat menyediakan perilaku default tanpa blok.
  • Blok do ... end menempel pada pemanggilan paling kiri, sehingga kurung kurawal atau tanda kurung menegaskan tempat blok pada rantai pemanggilan.

Referensi#