Tu écris [1, 2, 3].each { |n| puts n } sans y penser. Mais ce { |n| ... },
c’est quoi ? Un bloc : un morceau de code que tu passes à une méthode, et que
la méthode peut décider d’exécuter — quand elle veut, autant de fois qu’elle
veut. C’est le mécanisme qui rend Ruby si expressif.
yield : « exécute le bloc qu’on m’a donné »
Toute méthode peut recevoir un bloc, même sans le déclarer. Le mot-clé yield
l’appelle :
def deux_fois
yield
yield
end
deux_fois { puts "bonjour" }
# bonjour
# bonjourLa méthode ne sait pas ce que fait le bloc. Elle sait juste quand le déclencher. C’est une inversion : c’est la méthode qui rend la main au bloc, pas l’inverse.
On peut passer des valeurs au bloc
yield accepte des arguments, qui deviennent les paramètres du bloc (entre | |) :
def chaque_paire(liste)
i = 0
while i < liste.size
yield(liste[i], liste[i + 1])
i += 2
end
end
chaque_paire([1, 2, 3, 4]) { |a, b| puts "#{a} et #{b}" }
# 1 et 2
# 3 et 4C’est exactement ainsi que each fonctionne : la méthode pilote l’itération, le
bloc décide quoi faire de chaque élément. Tu délègues le contrôle du quoi,
tout en gardant le comment.
Et si aucun bloc n’est donné ?
block_given? te le dit, pour ne pas planter sur un yield dans le vide :
def saluer
if block_given?
yield "Ada"
else
"Bonjour Ada"
end
end
saluer # => "Bonjour Ada"
saluer { |nom| "Salut #{nom}!" } # => "Salut Ada!"Le bloc reste anonyme et attaché à un seul appel. Le jour où tu veux le stocker
dans une variable ou le passer plus loin, il te faut un Proc ou une lambda —
et là, les règles changent (sur le return, sur le nombre d’arguments).
C’est la suite logique : comprends le bloc et yield, et tu comprendras pourquoi
Ruby a aussi besoin des procs et des lambdas.
You write [1, 2, 3].each { |n| puts n } without thinking. But that { |n| ... },
what is it? A block: a chunk of code you pass to a method, which the method can
decide to run — whenever it wants, as many times as it wants. It’s the mechanism
that makes Ruby so expressive.
yield: “run the block I was given”
Any method can receive a block, even without declaring it. The yield keyword
calls it:
def twice
yield
yield
end
twice { puts "hello" }
# hello
# helloThe method doesn’t know what the block does. It only knows when to trigger it. It’s an inversion: the method hands control to the block, not the other way around.
You can pass values to the block
yield takes arguments, which become the block’s parameters (between | |):
def each_pair(list)
i = 0
while i < list.size
yield(list[i], list[i + 1])
i += 2
end
end
each_pair([1, 2, 3, 4]) { |a, b| puts "#{a} and #{b}" }
# 1 and 2
# 3 and 4This is exactly how each works: the method drives the iteration, the block
decides what to do with each element. You delegate control of the what, while
keeping the how.
What if no block is given?
block_given? tells you, so you don’t blow up on a yield into the void:
def greet
if block_given?
yield "Ada"
else
"Hello Ada"
end
end
greet # => "Hello Ada"
greet { |name| "Hi #{name}!" } # => "Hi Ada!"The block stays anonymous and tied to a single call. The day you want to store
it in a variable or pass it further along, you need a Proc or a lambda — and
there, the rules change (about return, about argument count).
It’s the logical next step: understand the block and yield, and you’ll
understand why Ruby also needs procs and lambdas.