I encountered the following Ruby code:
class MyClass
attr_accessor :items
...
def each
@items.each{|item| yield item}
end
...
end
According to my understanding yield executes code from block.
def name
puts "A yield will be called with id of 12"
yield 12
puts "A yield will be called with id of 14"
yield 14
end
name {|i| puts "I am called by yield name #{i}"}
Output:
A yield will be called with id of 12
I am called by yield name 12
A yield will be called with id of 14
I am called by yield name 14
How yield works?
So when the name
function runs wherever yield comes the block code runs. Which is name {|i| puts "I am called by yield name #{i}"}
You can see that there is a word yield 12
yield runs the code inside block passing 12 as value of i
.
Here is a game example for it:
def load_game
puts "Loading"
yield
end
load_game { puts "Game loaded" }
This will print game loaded
right after printing loading
:
Loading
Game Loaded
When you write a method that takes a block, you can use the yield
keyword to execute the block.
As an example, each
could have been implemented in the Array class like this:
class Array
def each
i = 0
while i < self.size
yield( self[i] )
i = i + 1
end
end
end
MyClass#each
takes a block. It executes that block once for each item in the instance's items
array, passing the current item as an argument.
It might be used like this:
instance = MyClass.new
instance.items = [1, 2, 3, 4, 5]
instance.each do |item|
puts item
end
The net effect is that calling .each on an instance of MyClass is the same as calling .each on the .items of that instance.
yield
tells ruby to call the block passed to the method, giving it its argument.
yield
will produce an error if the method wasn't called with a block where as return
statement don't produces error.
return
can only send single values where as Yield
return object of huge values.
As a newbie, looking through a number of the answers confused me until I hit Abhi's answer.
the yield command pauses executing the code in the method, and instead passes control back to the block of code that called it, executes that code, and then continues executing the rest of the method after that. Here's an example that clarified it for me:
def hello
puts "hello"
yield
puts "world"
end
hello do
puts "there"
end
Output:
hello
there
world
This is an example fleshing out your sample code:
class MyClass
attr_accessor :items
def initialize(ary=[])
@items = ary
end
def each
@items.each do |item|
yield item
end
end
end
my_class = MyClass.new(%w[a b c d])
my_class.each do |y|
puts y
end
# >> a
# >> b
# >> c
# >> d
each
loops over a collection. In this case it's looping over each item in the @items
array, initialized/created when I did the new(%w[a b c d])
statement.
yield item
in the MyClass.each
method passes item
to the block attached to my_class.each
. The item
being yielded is assigned to the local y
.
Does that help?
Now, here's a bit more about how each
works. Using the same class definition, here's some code:
my_class = MyClass.new(%w[a b c d])
# This points to the `each` Enumerator/method of the @items array in your instance via
# the accessor you defined, not the method "each" you've defined.
my_class_iterator = my_class.items.each # => #<Enumerator: ["a", "b", "c", "d"]:each>
# get the next item on the array
my_class_iterator.next # => "a"
# get the next item on the array
my_class_iterator.next # => "b"
# get the next item on the array
my_class_iterator.next # => "c"
# get the next item on the array
my_class_iterator.next # => "d"
# get the next item on the array
my_class_iterator.next # =>
# ~> -:21:in `next': iteration reached an end (StopIteration)
# ~> from -:21:in `<main>'
Notice that on the last next
the iterator fell off the end of the array. This is the potential pitfall for NOT using a block because if you don't know how many elements are in the array you can ask for too many items and get an exception.
Using each
with a block will iterate over the @items
receiver and stop when it reaches the last item, avoiding the error, and keeping things nice and clean.