What does the “yield” keyword do in Ruby?

后端 未结 8 1646
轮回少年
轮回少年 2020-12-24 01:22

I encountered the following Ruby code:

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


        
相关标签:
8条回答
  • 2020-12-24 02:04

    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

    0 讨论(0)
  • 2020-12-24 02:13

    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
    
    0 讨论(0)
  • 2020-12-24 02:16

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

    0 讨论(0)
  • 2020-12-24 02:20

    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.

    0 讨论(0)
  • 2020-12-24 02:24

    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

    0 讨论(0)
  • 2020-12-24 02:28

    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.

    0 讨论(0)
提交回复
热议问题