Why does Ruby use yield?

£可爱£侵袭症+ 提交于 2019-12-18 12:19:39

问题


I am new to Ruby. I have used a lot of C# and JavaScript which allow higher-order functions and I typically use them on a daily basis.

Ruby seems a little strange to me though. An each function might look like:

def each
    @items.each do |item|
        yield(item)
    end
end

items.each { |item| puts item }

Yet Ruby also has some support for higher-order functions. The above could be rewritten to something like:

def each(proc)
    @items.each do |item|
        proc.call item
    end
end

items.each -> (item) { puts item }        # Or...
items.each lambda { |item| puts item }

Or even:

def each(&proc)
    @items.each do |item|
        proc.call item
    end
end

# No difference in syntax.
items.each { |item| puts item }

Which is more on par with most other languages, and is just a few characters longer. Instead of explicitly passing in a block, everything seems to use yield.

yield itself seems crazy, magical, and mysterious. After all, it's going to the origin of the call and grabbing a block immediately following the call. This seems bizarre and unnatural, and I'm not aware of any parallel of this feature in another language.

So what's the deal with yield?


回答1:


Yield Passes Objects to a Method's Block

[Yield is] going to the origin of the call and grabbing a block immediately following the call.

Not really. yield passes an argument to a block; it doesn't "grab a block" or do anything with it. In other words, this:

def foo; yield self; end
foo { |x| x.inspect }                                       
# => "main"

Here, yield isn't doing anything but passing an argument to the block that is passed into the foo method. Every Ruby method supports an optional block—except when a block is actually mandatory—so the only "magic" is that the language allows a block to be passed even when it isn't explicitly declared as part of the method signature.

Further Examples

To see this implicit signature in action, consider this:

def foo; puts block_given?; end
foo { |x| x.inspect }

which will print "true" and return nil, which is the expected return value from the puts method.

Of course, without yield the block doesn't do anything at all. For example:

def foo; end
foo { |x| x.inspect }
# => nil



回答2:


Yield is Syntax Sugar

This example of yield:

def do_something_for_each(array)
  array.each do |el|
    yield(el)
  end
end

Is just syntax sugar for:

def do_something_for_each(array, &block)
  array.each do |el|
    block.call(el)
  end
end

Pick the syntax you like and run wild with it.




回答3:


One advantage of yield is it also lets you use next (like continue) and break. In other languages, for next, you might have to use return, and for break, you might have to (ab)use exceptions. It is arguably nicer to have built-in support for these sorts of operations.




回答4:


In most cases, you execute the block right there in the method, using yield.

The block is passed straight into the method, and the method can then call back to the block with the yield keyword.

def a_method(a, b)
a + yield(a, b)
end
a_method(1, 2) {|x, y| (x + y) * 3 } # => 10

When you call back to the block, you can provide values for its arguments, just like you do when you call a method. Also, like a method, a block returns the result of the last line of code it evaluates.



来源:https://stackoverflow.com/questions/14309815/why-does-ruby-use-yield

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