Yielding in an anonymous block
The lambda
is a closure and it seems to be capturing the block_given?
and block from its outer scope. This behavior does make sense as the block is, more or less, an implied argument to the outer method; you can even capture the block in a named argument if desired:
def def_test(&block)
frobnicate &block
end
So the block is part of the argument list even when it isn't named.
Consider this simple piece of code:
def f
lambda do
puts "\tbefore block"
yield if block_given?
puts "\tafter block"
end
end
puts 'Calling f w/o block'
x = f; x.call
puts
puts 'Calling f w/ block'
x = f { puts "\t\tf-block" }; x.call
puts
puts 'Calling f w/o block but x with block'
x = f; x.call { puts "\t\tx-block" }
puts
puts 'Calling f w/ block and x with block'
x = f { puts "\t\tf-block" }; x.call { puts "\t\tx-block" }
This produces the following for me with 1.9.2:
Calling f w/o block
before block
after block
Calling f w/ block
before block
f-block
after block
Calling f w/o block but x with block
before block
after block
Calling f w/ block and x with block
before block
f-block
after block
Furthermore, Proc#call
(AKA proc ===
) doesn't take a block:
prc === obj → result_of_proc
Invokes the block, with obj as the block‘s parameter. It is to allow a proc object to be a target of when clause in the case statement.
Compare the first line with the documentation for Enumerable#chunk
(for example):
enum.chunk {|elt| ... } → an_enumerator
The {...}
indicates that chunk
is documented to take a block, the lack of such notation for Proc#call
indicates that Proc#call
does not take a block.
This isn't exactly an authoritative answer but maybe it clears things up a little bit.
yield to an anonymous block two functions up
I think maybe the concept of yield
will be more clear if you look at an alternative syntax, which is converting the bloc to a proc argument.
For example, the following examples are the same
def my_each(arr)
arr.each { |x| yield x }
end
def my_each(arr, &blk)
arr.each { |x| blk.call(x) }
end
# Both are called the same way
my_each([1,2,3]) { |x| print x }
# => 123
When using yield
, the variable is available in the method without declaring it in the parameters list. Prepending an &
sign to a parameter converts it to a proc, so in the method it can be run with .call
.
Here's an example of providing a block to one method then executing it two scopes in:
def method_a(number, &blk)
method_b do
method_c do
blk.call(number)
end
end
end
def method_b(&blk)
blk.call
end
def method_c(&blk)
blk.call
end
method_a(1) { |num| puts num + 1 }
# => 2
Note that blk
is not a magic word - you can name the variable whatever you want.
Here's the same thing with yield:
def method_a(number)
method_b do
method_c do
yield number
end
end
end
def method_b
yield
end
def method_c
yield
end
method_a(1) { |num| puts num + 1 }
# => 2
I think using the &blk
syntax is clearer because it assigns a variable to the proc. Just because a proc is used in the method doesn't mean you have to ever run Proc.new
. The block is automatically converted to a proc.
In C#, why can't an anonymous method contain a yield statement?
Eric Lippert recently wrote a series of blog posts about why yield is not allowed in some cases.
- Part 1
- Part 2
- Part 3
- Part 4
- Part 5
- Part 6
EDIT2:
- Part 7 (this one was posted later and specifically addresses this question)
You will probably find the answer there...
EDIT1: this is explained in the comments of Part 5, in Eric's answer to Abhijeet Patel's comment:
Q :
Eric,
Can you also provide some insight into
why "yields" are not allowed inside an
anonymous method or lambda expression
A :
Good question. I would love to have
anonymous iterator blocks. It would be
totally awesome to be able to build
yourself a little sequence generator
in-place that closed over local
variables. The reason why not is
straightforward: the benefits don't
outweigh the costs. The awesomeness of
making sequence generators in-place is
actually pretty small in the grand
scheme of things and nominal methods
do the job well enough in most
scenarios. So the benefits are not
that compelling.The costs are large. Iterator
rewriting is the most complicated
transformation in the compiler, and
anonymous method rewriting is the
second most complicated. Anonymous
methods can be inside other anonymous
methods, and anonymous methods can be
inside iterator blocks. Therefore,
what we do is first we rewrite all
anonymous methods so that they become
methods of a closure class. This is
the second-last thing the compiler
does before emitting IL for a method.
Once that step is done, the iterator
rewriter can assume that there are no
anonymous methods in the iterator
block; they've all be rewritten
already. Therefore the iterator
rewriter can just concentrate on
rewriting the iterator, without
worrying that there might be an
unrealized anonymous method in there.Also, iterator blocks never "nest",
unlike anonymous methods. The iterator
rewriter can assume that all iterator
blocks are "top level".If anonymous methods are allowed to
contain iterator blocks, then both
those assumptions go out the window.
You can have an iterator block that
contains an anonymous method that
contains an anonymous method that
contains an iterator block that
contains an anonymous method, and...
yuck. Now we have to write a rewriting
pass that can handle nested iterator
blocks and nested anonymous methods at
the same time, merging our two most
complicated algorithms into one far
more complicated algorithm. It would
be really hard to design, implement,
and test. We are smart enough to do
so, I'm sure. We've got a smart team
here. But we don't want to take on
that large burden for a "nice to have
but not necessary" feature. -- Eric
How to yield return inside anonymous methods?
Ok so I did something like this which does what I wanted (some variables omitted):
public static void Run ( Action<float, EffectResult> action )
{
worker.DoWork += ( sender, e ) =>
{
foreach ( var effect in GlobalGraph.Effects )
{
var result = image.Apply (effect);
action (100 * ( index / count ), result );
}
}
};
and then in the call site:
GlobalGraph.Run ( ( p, r ) =>
{
this.Progress = p;
this.EffectResults.Add ( r );
} );
string concatenation and yielding block based on if modifier
You can't concatenate strings like that. The line yield(block) +
isn't a complete line. That's why you're getting the errors. Here are two possible fixes:
def wrapper(form, attr, options = {}, &block)
if block_given?
return yield(block) + form.label(form_label, class: "control-label")
end
form.label(form_label, class: "control-label")
end
Or this
def wrapper(form, attr, options = {}, &block)
content = ''
if block_given?
content = yield(block)
end
content + form.label(form_label, class: "control-label")
end
Ruby: yield block from a block?
I'm not sure if you can you can do that, but something similar would be:
In Ruby 1.8.6:
a = lambda { |my_proc|
puts 'in a'
my_proc.call
}
a.call(lambda { puts "in a's block" })
In Ruby 1.9.1, you can have block parameters
a = lambda { |&block|
puts 'in a'
block.call
}
a.call { puts "in a's block" }
Ruby: the yield inside of a block
The block is passed similarly to the argument of that function. This can be specified explicitly, like so:
class Test
def my_each(&block)
"abcdeabcabc".scan("a") do |x|
puts "!!! block"
yield x
# Could be replaced with: block.call(x)
end
end
end
Technically, it's exactly the same (puts
put in there for clarification), its presence is not checked the way it is usually done for arguments. Should you forget to give it a block, the function will halt on the first yield
it has to execute with exactly the same LocalJumpError
(at least, that's what I get on Rubinius). However, notice the "!!! block" in the console before it happens.
It works like that for a reason. You could check whether your function is given a block, if it is specified explicitly as above, using if block
, and then skip the yield
s. A good example of that is a content_tag
helper for Rails. Calls of this helper can be block-nested. A simplistic example:
content_tag :div do
content_tag :div
end
...to produce output like:
<div>
<div></div>
</div>
So, the block is executed "on top" (in terms of call stack) of your method. It is called each time a yield
happens as some sort of function call on a block. It's not accumulated anywhere to execute the block afterwards.
UPD:
The Enumerator
returned by many each
es is explicitly constructed by many iterators to save context of what should happen.
It could be implemented like this on my_each
:
class Test
def my_each(&block)
if block
"abcdeabcabc".scan("a") { |x| yield x }
else
Enumerator.new(self, :my_each)
end
end
end
Related Topics
Rails Devise - Current_User Is Nil
Ruby on Rails Map.Root Doesn't Seem to Be Working
Need to Use Add_Index on Migration for Belongs_To/Has_Many Relationship? (Rails 3.2, Active Record)
Grabbing Snapshots from Webcams in Ruby
How Get Best Performance Rails Requests Parallel Sidekiq Worker
Rails Helper - How to Get a Helper to Give Me a '<Br/>' (Or Other Markup)
Ruby: What Is the Order of Keys/Values Returned by Hash.Keys and Hash.Values Methods
Serialize Array with Strong_Parameters
What's the Difference Between /\P{Alpha}/I and /\P{L}/I in Ruby
How to Use Objects with Xsi:Types in Savon
Converting String "2½" (Two and a Half) into 2.5
How to Set Environment Variable Using Chef
How to Get the Number of Elements Having Same Attribute in HTML in Watir
How to Search Array Through Ransack Gem
Drop-Down-Menu for Many-To-Many Relation in Rails Using Nested Attributes
How to Force a Gem's Dependencies in Gemfile