What does it mean to yield within a block?

2020-03-23 18:15发布

  def any?
    if block_given?
      method_missing(:any?) { |*block_args| yield(*block_args) }
    else
      !empty?
    end
  end

In this code from ActiveRecord, what is the purpose of a yield statement that exists within a block?

标签: ruby
3条回答
▲ chillily
2楼-- · 2020-03-23 18:42

Basically if the current method has been given a code-block (by the caller, when it was invoked), the yield executes the code block passing in the specified parameters.

[1,2,3,4,5].each { |x| puts x }

Now { |x| puts x} is the code-block (x is a parameter) passed to the each method of Array. The Array#each implementation would iterate over itself and call your block multiple times with x = each_element

pseudocode
def each
  #iterate over yourself
    yield( current_element )
end

Hence it results

1
2
3
4
5

The *block_args is a Ruby way to accept an unknown number of parameters as an array. The caller can pass in blocks with different number of arguments.

Finally let's see what yield within a block does.

class MyClass
  def print_table(array, &block)
    array.each{|x| yield x}
  end
end

MyClass.new.print_table( [1,2,3,4,5] ) { |array_element| 
    10.times{|i| puts "#{i} x #{array_element} = #{i*array_element}" }
    puts "-----END OF TABLE----"
  }

Here Array#each yields each element to the block given to MyClass#print_table...

查看更多
乱世女痞
3楼-- · 2020-03-23 18:42

This helped me understand: yield is a way to insert blocks into a method you already wrote, which means "execute something here". For instance,

Without parameter

def original_method
   puts "a"
   puts "b"
end

What if you want to execute some unknown block between those two p?

def updated_method
   puts "a"
   yield
   puts "b"
end

# these two are the same:
updated_method { puts "execute something here" }
updated_method do puts "execute something here" end

The result would be

a
execute something here
b

You can have as many yields in a method as you like.

With parameter

def updated_method
   puts "a"
   yield
   puts "b"
end

What if you want to execute some unknown block on the string "Execute something on me!"?

def updated_method_with_argument
   puts "a"
   yield("Execute something on me!")
   puts "b"
end

updated_method_with_argument do |argument|
  puts "This argument gets put out: " << argument
end

The result would be

a
This argument gets put out: Execute something on me!
b
查看更多
何必那么认真
4楼-- · 2020-03-23 19:08

It does not mean anything special. It's just a yield like any other yield.

def test_method
  ["a", "b", "c"].map {|i| yield(i) }
end

p test_method {|i| i.upcase }
# => ["A", "B", "C"]

In the code snippet from active record, the purpose is to yield every time the block of any? is called.

查看更多
登录 后发表回答