csiszarattila.com / Rubysztán

Ruby blokkok közelebbről

A mai célünk megtanulni, hogy a következő kifejezés:

Array.new(3) { n += 1; n * 10 }
=>[10, 20, 30]

mit is jelent, hogyan is működik pontosan.

A Ruby fontos nyelvi elemeit jelentik a blokkok, amelyet szinte minden, Ruby-t használó programozó ismer, a kérdés csak csupán az, tudja is hogy hogyan is működik?

Sokak számára rémisztőnek tűnnek, de a blokkok működésének megértése nem igényel bonyolult logikát,

ugyanakkor használatuk nagy segítséget jelenthet bizonyos problémák egyszerű megoldásában.

A következőekben példákkal szeretném bemutatni, hogyan használhatjuk a blokkokat Rubyban.

Hogyan készítsünk saját blokkot kezelő metódust

Azt már tudjuk hogyan adhatunk át blokkot egy metódusnak:

Array(1, 2, 3).each() {
  # Ez itt egy blokk
}

vagy más formában:

Array(1, 2, 3).each() do
  # Ez is egy blokk
end
Én személy szerint a do end kulcsszavas megadást többsoros, a kapcsos zárójeleket pedig rövid, egy sorban is elférő utasítású blokkok esetében használom.

Vizsgáljuk meg ezek működését kicsit közelebbről:

class MyBlock
  def handle_block  (2)
    puts "A metódus megkapta a vezérlést"
    yield   (1)
    puts "A blokk visszaadta a vezérlést a blokknak"
  end
end
MyBlock.handle_block() do
  puts "A metódus átadta a vezérlést a blokknak"
end

Első ízben a metódusunk hívódik meg(1), a (2) yield nyelvi elem szolgál a blokkok 'vezérlésére'.

Amint az látható a metódusokban ezzel tudjuk a program további futását átadni a kapott blokknak,

majd ha az végzett a feladataival újból visszaadja a vezérlést a metódusunknak. Természetesen a yieldet akárhányszor meghívhatjuk a metódusunkban: az mindannyiszor átadja a vezérlést a blokknak, majd visszatér. Most nézzük meg hogyan tudunk paramétereket is átadni a blokkunknak:
class MyBlock
  def handle_block
    puts "A metódus megkapta a vezérlést"
    n=1
    while(n < 4)
      yield(n)
      n += 1
    end
    puts "A blokk visszaadta a vezérlést a blokknak"
  end
end

MyBlock.handle_block() { |value|  puts value }
=> 1 2 3

A metódus a yield(n) meghívásával ad át paramétereket a blokknak, míg a blokk a |value| kifejezéssel vesz át paramétereket. A blokk esetében tehát a neki átadott paramétereket a || (pipes - csövek) jelek közé kell tennünk, a yield()-t pedig a megfelelő paraméter sorrenddel kell ellátnunk.

Blokk visszadott értéke

De hogyan tudjuk a blokk visszaadott értékét felhasználni. Például tételezzük fel, hogy egy blokk segítségével képezünk értékeket és azok segítségével létrehozzuk az objektum adott attribútumait.

class Szazadok
  attr_reader :values
  def initialize(size)
    n = 0; @ertekek = []
    size.times do
      n += 1
      value = yield(n) (1)
      @values << value
    end
    end
end

Szazadok.new(3){ |n| n.to_s.upper+".század"(2) }
p Szazadok.values => ["1.század","2.század","3.század"]

A lényeg az (1)-sel megjelölt kifejezésnél van, azaz a blokk visszatérő értékét(2) a yield kifejezés(metódus?) visszatérési értéke tartalmazza. Emlékezzünk csak, hogy a Ruby nyelvben egy kódblokk esetében a visszatérési érték mindig a legutolsónak kiértékelt sor visszaadott értéke - ha nem bíráljuk felül egy megfelelő helyre beszúrt return kulcsszóval.

A initialize metódus pedig a neki átadott értékből (3) tudja, hogy hányszor kell a neki átadott blokkot meghívnia.

Természetesen a fenti programot ennél sokkal egyszerűbben is megoldhattuk volna, ezúttal csak a blokkok visszatérési értékének bemutatása volt a fő célom.

Pontosan ez a működés zajlik le, amikor meghívjuk a bejegyzés elején bemutatott példát:

Array.new(3) { n += 1; n * 10 }
=>[10, 20, 30]