What Does It Mean to Yield Within a Block

What does it mean to yield within a block?

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...

Is it safe to yield from within a with block in Python (and why)?

I don't really understand what conflict you're asking about, nor the problem with the example: it's fine to have two coexisting, independent handles to the same file.

One thing I didn't know that I learned in response to your question it that there is a new close() method on generators:

close() raises a new GeneratorExit exception inside the generator to terminate the iteration. On receiving this exception, the generator’s code must either raise GeneratorExit or StopIteration.

close() is called when a generator is garbage-collected, so this means the generator’s code gets one last chance to run before the generator is destroyed. This last chance means that try...finally statements in generators can now be guaranteed to work; the finally clause will now always get a chance to run. This seems like a minor bit of language trivia, but using generators and try...finally is actually necessary in order to implement the with statement described by PEP 343.

http://docs.python.org/whatsnew/2.5.html#pep-342-new-generator-features

So that handles the situation where a with statement is used in a generator, but it yields in the middle but never returns—the context manager's __exit__ method will be called when the generator is garbage-collected.


Edit:

With regards to the file handle issue: I sometimes forget that there exist platforms that aren't POSIX-like. :)

As far as locks go, I think Rafał Dowgird hits the head on the nail when he says "You just have to be aware that the generator is just like any other object that holds resources." I don't think the with statement is really that relevant here, since this function suffers from the same deadlock issues:

def coroutine():
lock.acquire()
yield 'spam'
yield 'eggs'
lock.release()

generator = coroutine()
generator.next()
lock.acquire() # whoops!

Yielding inside a block

Proper functioning of the included Enumerable interface's methods requires that the class implement an each method that yields successive values. The implementation in your sample code effectively delegates this logic to that of Array's each method (since @players is an Array).

Replacing yield with return would result in no values being provided, and bypassing any code block that was passed in.

You could experiment with the each implementation to verify this.

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 yields. 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 eaches 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

Blocks and yields in Ruby

Yes, it is a bit puzzling at first.

In Ruby, methods can receive a code block in order to perform arbitrary segments of code.

When a method expects a block, you can invoke it by calling the yield function.

Example:

Take Person, a class with a name attribute and a do_with_name method. When the method is invoked it will pass the name attribute to the block.

class Person 
def initialize( name )
@name = name
end

def do_with_name # expects a block
yield( @name ) # invoke the block and pass the `@name` attribute
end
end

Now you can invoke this method and pass an arbitrary code block.

person = Person.new("Oscar")

# Invoking the method passing a block to print the value
person.do_with_name do |value|
puts "Got: #{value}"
end

Would print:

Got: Oscar

Notice the block receives as a parameter a variable called value. When the code invokes yield it passes as argument the value of @name.

yield( @name )

The same method can be invoked with a different block.

For instance to reverse the name:

reversed_name = ""

# Invoke the method passing a different block
person.do_with_name do |value|
reversed_name = value.reverse
end

puts reversed_name

=> "racsO"

Other more interesting real life examples:

Filter elements in an array:

 days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]  

# Select those which start with 'T'
days.select do | item |
item.match /^T/
end

=> ["Tuesday", "Thursday"]

Or sort by name length:

 days.sort do |x,y|
x.size <=> y.size
end

=> ["Monday", "Friday", "Tuesday", "Thursday", "Wednesday"]

If the block is optional you can use:

yield(value) if block_given?

If is not optional, just invoke it.

You can try these examples on your computer with irb (Interactive Ruby Shell)

Here are all the examples in a copy/paste ready form:

class Person 
def initialize( name )
@name = name
end

def do_with_name # expects a block
yield( @name ) # invoke the block and pass the `@name` attribute
end
end

person = Person.new("Oscar")

# Invoking the method passing a block to print the value
person.do_with_name do |value|
puts "Got: #{value}"
end

reversed_name = ""

# Invoke the method passing a different block
person.do_with_name do |value|
reversed_name = value.reverse
end

puts reversed_name

# Filter elements in an array:
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]

# Select those which start with 'T'
days.select do | item |
item.match /^T/
end

# Sort by name length:
days.sort do |x,y|
x.size <=> y.size
end

Trouble yielding inside a block/lambda

Lambdas don't implicitly accept blocks like regular methods do, so your func1 can't yield. Do this instead:

func1 = lambda do |x, &blk|
for i in 1 .. 5
blk.call(x * i)
end
end

Specifically, I believe this is because yield would send control back to the caller's block, which would not include lambda invocations. So the following code works like you "expect":

def foo
(lambda { |n| yield(n) }).call(5)
end
foo { |f| puts f } # prints 5

yield inside with block in python function

Because you aren't actually iterating over the generator:

You have something similar to this in your code:

doSmth() # simply creates generator without advancing it

without the for x in doSmth()

What does 'yield called out of block' mean in Ruby?

The problem is that the times method expects to get a block that it will yield control to. However you haven't passed a block to it. There are two ways to solve this. The first is to not use times:

mySet = (1..numOfCuts).map{ rand(seqLength) }

or else pass a block to it:

mySet = []
numOfCuts.times {mySet.push( rand(seqLength) )}

Yield within Set to eliminate in an Array

In Ruby, when you are putting yield keyword inside any method(say #bar), you are explicitly telling #bar that, you will be using a block with the method #bar. So yield knows, inside the method block will be converted to a Proc object, and yield have to call that Proc object.

Example :

def bar
yield
end

p bar { "hello" } # "hello"
p bar # bar': no block given (yield) (LocalJumpError)

In the uniq_by method, we did not do anything to handle block argument. How is the passed argument handled by uniq_by method?

You did do, that is you put yield. Once you will put this yield, now method is very smart to know, what it supposed to so. In the line Messages.all.uniq_by { |h| h.body } you are passing a block { |h| h.body }, and inside the method definition of uniq_by, that block has been converted to a Proc object, and yield does Proc#call.

Proof:

def bar
p block_given? # true
yield
end

bar { "hello" } # "hello"

Better for understanding :

class Array
def uniq_by
seen = Set.new
select{ |x| seen.add?( yield( x ) ) }
end
end

is same as

class Array
def uniq_by
seen = Set.new
# Below you are telling uniq_by, you will be using a block with it
# by using `yield`.
select{ |x| var = yield(x); seen.add?(var) }
end
end

Read the doc of yield

Called from inside a method body, yields control to the code block (if any) supplied as part of the method call. If no code block has been supplied, calling yield raises an exception. yield can take an argument; any values thus yielded are bound to the block's parameters. The value of a call to yield is the value of the executed code block.



Related Topics



Leave a reply



Submit