What does the “yield” keyword do in Ruby?

醉酒当歌 提交于 2019-11-28 18:48:28

问题


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.


回答1:


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.




回答2:


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



回答3:


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.




回答4:


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.




回答5:


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




回答6:


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




回答7:


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




回答8:


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 


来源:https://stackoverflow.com/questions/4321737/what-does-the-yield-keyword-do-in-ruby

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!