What does the "yield" keyword do in Ruby?
Asked Answered
S

8

39

I encountered the following Ruby code:

class MyClass
    attr_accessor :items
    ...
    def each
        @items.each{|item| yield item}
    end
    ...
end

What does the each method do? In particular, I don't understand what yield does.

Serving answered 1/12, 2010 at 6:17 Comment(0)
R
37

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.

Ro answered 1/12, 2010 at 7:41 Comment(2)
Did you meant Begin-End Block as stated here . I am new to ruby so trying to find out what did you meant by block there.Snooty
You will also see return to_enum(:each) unless block_given? in #each which will return an Enumerator when no block is given allowing things such as collection.each.take(10).Doing
H
17

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
Hilbert answered 1/12, 2010 at 8:8 Comment(0)
R
10

A Ruby method that receives a code block invokes it by calling it with the yield keyword. It can be used to iterate over a list but it is not a iterator like what you find in other some other languages.

Here is a good explanation that explains it better than I would ever be able to.

Rectrix answered 1/12, 2010 at 7:42 Comment(1)
fwiw - I found this page to give a simpler explanation of {code}yield{code} specificallySlowmoving
P
9

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

Plan answered 3/2, 2017 at 18:39 Comment(0)
M
6

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

Mather answered 11/9, 2016 at 5:47 Comment(0)
R
5

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.

Romeyn answered 1/9, 2015 at 12:52 Comment(0)
M
4

The net effect is that calling .each on an instance of MyClass is the same as calling .each on the .items of that instance.

Moretta answered 1/12, 2010 at 6:27 Comment(0)
M
3

As cpm said its taking the block and executing it

Simple example:

def my_method
  yield
end


my_method do
  puts "Testing yield"
end

Testing yield
=> nil 
Markel answered 20/8, 2014 at 18:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.