Block

Block do...end dan { }, mekanika yield, serta argumen block berbentuk |x|.

Daftar isi

Blok (block) adalah potongan kode yang diserahkan kepada pemanggilan metode untuk dijalankan oleh metode penerimanya. Hampir semua kode Ruby bergantung pada blok, mulai dari iterator each dan map pada koleksi hingga perulangan loop. Topik ini membahas dua bentuk penulisan blok, mekanika kata kunci yield yang menjalankannya, cara blok menerima nilai melalui argumen berbentuk |x|, serta sifat blok yang membaca variabel di lingkungannya. Setelah membacanya, setiap pemanggilan metode yang menyertakan blok pada kode sehari-hari dapat diurai lengkap dengan alur kendalinya.

Dua bentuk penulisan blok#

Blok ditulis dalam dua bentuk, yaitu di antara do dan end atau di antara sepasang kurung kurawal { }. Keduanya berperilaku sama dan dapat digunakan pada metode mana pun yang menerima blok. Konvensi penulisan yang lazim menggunakan kurung kurawal untuk blok satu baris, sedangkan do ... end digunakan untuk blok yang isinya lebih dari satu baris. Program berikut menampilkan kedua bentuk tersebut pada iterator each dan map.

ruby dua-bentuk.rb
# Dua bentuk penulisan block pada iterator
anggota = ["Ali", "Budi", "Citra"]

anggota.each do |nama|
  puts "Piket: #{nama}"
end

anggota.each { |nama| puts "Bentuk kurawal: #{nama}" }

puts anggota.map { |nama| nama.length }
text Keluaran
Piket: Ali
Piket: Budi
Piket: Citra
Bentuk kurawal: Ali
Bentuk kurawal: Budi
Bentuk kurawal: Citra
3
4
5

Pemanggilan anggota.each menyerahkan blok yang berisi puts, sehingga badan blok berjalan sekali untuk setiap elemen array. Bentuk kurawal pada pemanggilan kedua melakukan hal yang sama dengan penulisan yang lebih padat. Pada baris terakhir, metode map menjalankan blok { |nama| nama.length } untuk setiap elemen dan mengumpulkan nilai kembaliannya menjadi array baru, yang kemudian dicetak satu angka per baris oleh puts. Pola metode dengan blok seperti inilah yang digunakan sehari-hari pada pengolahan koleksi.

yield menjalankan blok#

Di dalam metode, kata kunci yield menjalankan blok yang menyertai pemanggilan metode tersebut. Posisi yield menentukan titik di mana badan blok berjalan, dan setiap yield menjalankan blok sekali. Metode berikut memanggil yield dua kali, sehingga badan blok juga berjalan dua kali.

ruby dua-kali.rb
def dua_kali
  yield
  yield
end

dua_kali do
  puts "Baris dicatat"
end
text Keluaran
Baris dicatat
Baris dicatat

Metode dua_kali tidak berisi logika apa pun tentang pekerjaan yang dijalankan, sedangkan blok menyediakan baris yang dicetak. Kendali berpindah dari badan metode ke blok setiap kali yield dieksekusi, lalu kembali ke badan metode setelah blok selesai. Diagram berikut merangkum alur tersebut.

Pemanggilan dua_kali dengan blockBadan metodedef dua_kaliyieldyieldendBlockdoputs "Baris dicatat"endyieldkendali kembali
Alur kendali antara badan metode dan block saat yield dijalankan

Alur dua arah ini berlaku untuk semua metode penerima blok, termasuk each dan times yang memanggil yield dari dalam perulangannya. Blok tidak pernah berjalan dengan sendirinya, melainkan hanya pada titik yield di badan metode yang memanggilnya. Pemahaman alur ini menjadi dasar untuk membaca setiap metode penerima blok.

Nilai kembalian blok#

Eksekusi yield bernilai sesuai nilai kembalian blok, sehingga metode dapat menggunakan hasil pekerjaan blok untuk melanjutkan prosesnya. Nilai kembalian blok adalah nilai ekspresi terakhir di dalam badannya, sebagaimana metode. Contoh berikut menyisipkan yield di dalam interpolasi string, sehingga hasil blok langsung menjadi bagian kalimatnya.

ruby laporan.rb
def laporan
  "Nilai tertinggi: #{yield}"
end

puts laporan { [78, 90, 85].max }
text Keluaran
Nilai tertinggi: 90

Pada pemanggilan tersebut, interpolasi menjalankan yield, blok menghitung nilai tertinggi dari tiga nilai ujian, dan nilai 90 mengisi kalimat laporan. Metode menyediakan kerangka kalimat, sedangkan blok menyediakan perhitungan. Pola yang sama digunakan metode map, yaitu mengumpulkan nilai kembalian blok dari setiap elemen menjadi array baru, sehingga kebiasaan mengakhiri blok dengan ekspresi yang ingin dikembalikan penting untuk dijaga.

Argumen blok berbentuk |x|#

Nilai yang diserahkan kepada blok diterima melalui daftar parameter yang ditulis di antara dua garis vertikal, misalnya |x|. Metode yang memanggil yield dapat menyertakan nilai, dan setiap nilai mengisi parameter blok menurut urutannya. Pada hash, iterator each menyampaikan kunci dan nilai setiap entri sebagai dua nilai terpisah, sehingga blok menerima dua parameter sekaligus.

ruby rekap-nilai.rb
rekap = { "Ali" => 78, "Budi" => 65, "Citra" => 90 }

rekap.each do |nama, nilai|
  puts "#{nama}: #{nilai}"
end
text Keluaran
Ali: 78
Budi: 65
Citra: 90

Parameter nama menerima kunci dan nilai menerima angkanya, dengan tanda koma sebagai pemisah di antara keduanya. Jumlah parameter blok tidak harus persis sama dengan jumlah nilai yang disampaikan metode, karena Ruby mengisi parameter yang tidak mendapat nilai dengan nil dan membuang kelebihan nilai tanpa pesan. Meski begitu, jumlah parameter yang sesuai dengan nilai yang disampaikan tetap menjadi bentuk yang paling jelas untuk dibaca.

Blok melihat variabel sekitarnya#

Blok membaca dan mengubah variabel lokal yang berada di sekitar tempat blok itu ditulis, sifat yang membedakan blok dari badan metode. Badan metode memiliki lingkup variabel lokal sendiri dan tidak melihat variabel di luarnya, sedangkan blok berbagi lingkup dengan kode yang melingkupinya. Sifat ini digunakan untuk menempatkan penghitung di luar perulangan, sementara blok memperbarui nilainya di dalam badan perulangan.

ruby penghitung-kata.rb
# Menghitung kemunculan kata tertentu dengan block
teks = "block menyelesaikan pekerjaan kecil, dan block tersedia di mana-mana"
jumlah = 0

teks.split.each do |satu|
  jumlah += 1 if satu == "block"
end

puts "Kata block muncul #{jumlah} kali"
text Keluaran
Kata block muncul 2 kali

Variabel jumlah didefinisikan di luar blok, diperbarui di dalam blok, dan nilainya tersisa setelah perulangan berakhir sehingga dapat dicetak. Pembaruan tersebut hanya mungkin karena blok berbagi lingkup dengan kode yang melingkupinya. Sebaliknya, parameter blok seperti satu hanya hidup di dalam blok, sehingga tidak meninggalkan variabel sisa setelah perulangan selesai.

Kesalahan umum#

Kesalahan yang paling sering terjadi pada mekanika blok adalah memanggil metode yang mengandung yield tanpa menyertakan blok. Kasus ini diperagakan dengan berkas yang sengaja mengandung kesalahan, lalu dijalankan dari terminal untuk melihat pesan error aslinya. Penanda # tidak-dijalankan pada baris pertama potongan kode bukan bagian program, melainkan tanda bahwa kode diperagakan lewat berkasnya.

ruby Berkas yield-tanpa-block.rb
# tidak-dijalankan

def catat(judul)
  puts "== #{judul} =="
  yield
end

catat("Rekap harian")
bash
# menjalankan berkas yang memanggil yield tanpa block
ruby yield-tanpa-block.rb
text Keluaran
yield-tanpa-block.rb:3:in 'Object#catat': no block given (yield) (LocalJumpError)
	from yield-tanpa-block.rb:6:in '<main>'
== Rekap harian ==

Pesan no block given (yield) menyatakan bahwa yield menemui pemanggilan tanpa blok, dan kelas errornya adalah LocalJumpError. Baris == Rekap harian == tetap tercetak karena letaknya sebelum yield, sedangkan eksekusi berhenti pada saat yield dijalankan. Perbaikannya adalah menyertakan blok pada setiap pemanggilan metode yang mengandung yield, atau memeriksa block_given? terlebih dahulu apabila pemanggilan tanpa blok memang boleh terjadi.

Ringkasan#

  • Blok ditulis di antara do ... end atau { }, dan keduanya berperilaku sama pada pemanggilan yang sama.
  • yield menjalankan blok pada posisi di mana yield itu berada, dan nilai kembalian blok menjadi nilai yield.
  • Nilai yang diserahkan kepada blok diterima parameter di antara dua garis vertikal, misalnya |nama, nilai|.
  • Blok berbagi lingkup dengan kode di sekitarnya, sedangkan parameternya hanya hidup di dalam blok.

Referensi#