Using yield

J

Joe Van Dyk

I come from a heavy C++ background, discovered Ruby a few months ago and
love it.

I've found that using blocks is a very natural thing. However, I have not
once used 'yield'. I'm sure that there are events when using yield would be
helpful, but I have no clue when it would be appropriate to use.

Thoughts? When do you use the 'yield' statement in code?

Joe
 
S

Sam Stephenson

When do you use the 'yield' statement in code?

When you want to write methods that take blocks.

| def repeatedly_write(times, &block)
| times.times do |i|
| puts yield(i)
| end
| end
|
| repeatedly_write(5) do |i|
| "iteration #{i}"
| end

prints:
iteration 0
iteration 1
iteration 2
iteration 3
iteration 4

In this example, ``yield(i)'' and ``block.call(i)'' are equivalent.

Sam
 
T

Tim Hunter

Joe said:
I come from a heavy C++ background, discovered Ruby a few months ago and
love it.

I've found that using blocks is a very natural thing. However, I have not
once used 'yield'. I'm sure that there are events when using yield would
be helpful, but I have no clue when it would be appropriate to use.

Thoughts? When do you use the 'yield' statement in code?

Joe

I use it with methods that create resources that need to be cleaned up
after, the way File.open closes the file for you after the block
terminates. If you're coming from C++, think destructors.

Here's an example from RMagick. The Image#view method extracts a rectangle
of pixels from an image and yields to a block (if present). Within the
block you can address the pixels using [j] indexes. When the block ends,
if any of the pixels have been modified all the pixels are stored
("sync'd") back to the image. The code looks like this:

def view(x, y, width, height)
view = View.new(self, x, y, width, height)
if block_given?
begin
yield(view)
ensure
view.sync
end
return nil
else
return view
end
end

You use #view like this:

image.view(5, 10, 20, 20) do |view|
view[5][7] = 'yellow'
# other useful stuff...
end
 
D

David A. Black

Hi --

When you want to write methods that take blocks.

| def repeatedly_write(times, &block)

You don't need &block there, since you don't use the block directly
(you just yield to it).


David
 
D

David G. Andersen

I come from a heavy C++ background, discovered Ruby a few months ago and
love it.

I've found that using blocks is a very natural thing. However, I have not
once used 'yield'. I'm sure that there are events when using yield would be
helpful, but I have no clue when it would be appropriate to use.

Thoughts? When do you use the 'yield' statement in code?

I use it to pass in an additional filtering routine to
a database selection class. The selection thing knows some
basic stuff like time, but if you want to filter
more precisely,
you can pass it a block:

db_generator = new thingy(start_time, end_time) { |x|
x.attr1 == "frog" && x.attr2 >= 5 }


and then in later code you can grab stuff from the generator
and it's filtered in the way you specified.

-Dave
 
G

gabriele renzi

Joe Van Dyk ha scritto:
Yes. What types of methods generally should take blocks?

My little list:
[1] those when behaviour can be further specified.
i.e. #sort and #sort_by can get a block to specify how to sort an object

[2] those where a resource could be freed
i.e. constructors like File.new or Socket.new get a block so that the
undelying resource would be freed when the block ends instead of waiting
for the gc.

[3] those where one would like to lazyly use/reuse a snippet of code
given from the user

i.e. set_trace_func

There are surely others, the best way to get the felling is probably
looking at existing ruby code.
 
B

Brian Schröder

I come from a heavy C++ background, discovered Ruby a few months ago and
love it.

I've found that using blocks is a very natural thing. However, I have not
once used 'yield'. I'm sure that there are events when using yield would be
helpful, but I have no clue when it would be appropriate to use.

Thoughts? When do you use the 'yield' statement in code?

Joe

Yield is just a shortcut for block.call.

If I am Lazy, and I don't need to pass the block further I use yield. But I
believe that normally using block.call is more explicit and therefore
preferable.

It may be the case that block.call has a small performance penalty compared
with yield because the block needs to get bound to a variable and therefore be
turned into an object.

Regards,

Brian
 
D

David A. Black

Hi --

Yield is just a shortcut for block.call.

If I am Lazy, and I don't need to pass the block further I use yield. But I
believe that normally using block.call is more explicit and therefore
preferable.

'yield' is equally explicit, and equally unambiguous. It tells you
exactly as much as block.call does. I wouldn't worry about the
laziness part of it; if you measure laziness by power per word of
code, all Ruby programmers are already massively lazy :)


David
 
J

Joel VanderWerf

Brian said:
Yield is just a shortcut for block.call.

Yield has an advantage: rdoc can recognize it and generate docs for it,
including the names of the variables that are yielded to the block.

The speed advnatage of yield can also be significant, IMHO.
 
B

Bill Kelly

Hi,

From: "Joe Van Dyk said:
What types of methods generally should take blocks?

My most recent use of yield is in a tree-structured database,
where I wanted to be able to perform {some operation} on a
node and all its children. So,

def apply_all(&block)
yield self
@children.each_value {|n| n.apply_all(&block) } if @children
end

The above is a method of the database node class. It
yields itself, and recursively calls apply_all on all of
its children, so they can yield themselves, etc....
So I can use it like:

some_node.apply_all {|n| n.some_method(foo) }

...to call some_method on some_node and all of its children.



My second most recent use of yield was to do some deferred
processing.

@server.rcon_cmd("dmflags #{dmflags}") do |resp|
@server_state['dmflags'] = dmflags.to_s if resp
end

The above wants to send a command to a server, and when the
response is later received, the block is called to perform
some further processing.

The command gets queued and is processed on different
thread. The block, above, is also passed along...
So that, eventually, when the command has been sent to
the server and the server's response has been received,
the block will be called and passed the server's response
as its argument.

I was particularly thrilled with Ruby when writing this,
because originally my code had been written to NOT queue
the commands to the server, but to send the command
immediately and do a blocking wait for the reply... Like,

if resp = @server.rcon_cmd("dmflags #{dmflags}")
@server_state['dmflags'] = dmflags.to_s
end

So I already had a bunch of code like the above, before I
realized that I really wanted to queue the commands and
process their result later. And you can see how little
I needed to change my code to make that happen!


Hope this helps, just a couple ways I've used yield recently.


Regards,

Bill
 
J

Jim Weirich

My most recent use of yield is in a tree-structured database,
where I wanted to be able to perform {some operation} on a
node and all its children.  So,

  def apply_all(&block)
    yield self
    @children.each_value {|n| n.apply_all(&block) } if @children
  end

This is a very common use of blocks. You might consider the following
modification, it may (or may not) be useful in your application.

Change the name of "apply_all" to "each", and then include the module
Enumerable into your class. Then you can do things like (assuming "name"
might be a method on your node) ....

some_node.collect { |n| n.name } # Get a list of all the names
some_node.find { |n| n.name =~ /^Bill/ } # Find a matching node
 
B

Bill Kelly

From: "Jim Weirich said:
Change the name of "apply_all" to "each", and then include the module
Enumerable into your class. Then you can do things like (assuming "name"
might be a method on your node) ....

some_node.collect { |n| n.name } # Get a list of all the names
some_node.find { |n| n.name =~ /^Bill/ } # Find a matching node

Nice, thanks !!


Regards,

Bill
 
R

Robert Klemme

Joe Van Dyk said:
I come from a heavy C++ background, discovered Ruby a few months ago and
love it.

I've found that using blocks is a very natural thing. However, I have not
once used 'yield'. I'm sure that there are events when using yield would
be
helpful, but I have no clue when it would be appropriate to use.

Thoughts? When do you use the 'yield' statement in code?

When implementing Enumerable classes

class Endless
include Enumerable
def each
i = 0
loop do
yield i
i += 1
end
end
end

More generally when you don't need direct access to the block given and when
you don't need to forward it to another method call. I think yield is
faster than the block form also.

Regards

robert
 
F

Florian Gross

Robert said:
More generally when you don't need direct access to the block given and
when you don't need to forward it to another method call. I think yield
is faster than the block form also.

But note that you can also forward blocks without using the &block syntax:

irb(main):022:0> def five_times
irb(main):023:1> 5.times { |index| yield index }
irb(main):024:1> end
=> nil
irb(main):025:0> five_times { |i| puts "Hello ##{i}" }
Hello #0
Hello #1
Hello #2
Hello #3
Hello #4

I'd expect this to be slower than the &block syntax, but I have not
benchmarked it. Maybe the &block syntax is also less clutter to look at
and thus easier to understand.
 
B

Brian Schröder

Hi --



'yield' is equally explicit, and equally unambiguous. It tells you
exactly as much as block.call does. I wouldn't worry about the
laziness part of it; if you measure laziness by power per word of
code, all Ruby programmers are already massively lazy :)

I see it as not as explicit, as it does not document in the method signature
that the message takes a block.

Regards,

Brian
 
D

David A. Black

Hi --

But note that you can also forward blocks without using the &block syntax:

irb(main):022:0> def five_times
irb(main):023:1> 5.times { |index| yield index }
irb(main):024:1> end
=> nil
irb(main):025:0> five_times { |i| puts "Hello ##{i}" }
Hello #0
Hello #1
Hello #2
Hello #3
Hello #4

I'd expect this to be slower than the &block syntax, but I have not
benchmarked it. Maybe the &block syntax is also less clutter to look at
and thus easier to understand.

Wouldn't the &block form be more clutter? Well, not that it's so
much clutter in either case, but I think it would be slightly wordier
(def five_time(&block) etc.)


David
 
D

David A. Black

Hi --

I see it as not as explicit, as it does not document in the method signature
that the message takes a block.

That actually doesn't tell you much, though, in practical terms. Any
method can have a &block argument, whether it calls it or not -- but,
more importantly, the presence of a &block argument doesn't tell you
what the block actually does. So its value as documentation is
somewhat limited; you still have to either read the code or the
documentation before you can call the method properly.


David
 
R

Robert Klemme

David A. Black said:

13:21:36 [robert.klemme]: ruby yield-vs-block.rb
user system total real
yield 0.250000 0.000000 0.250000 ( 0.249000)
block 0.125000 0.000000 0.125000 ( 0.124000)
13:21:44 [robert.klemme]: cat yield-vs-block.rb

require 'benchmark'
include Benchmark

REP = 100000

def bl_yield(n)
n.times {|i| yield i}
end

def bl_fwd(n,&b)
n.times(&b)
end

bm(20) do |b|
b.report("yield") do
bl_yield(REP) {|i| i + 1}
end

b.report("block") do
bl_fwd(REP) {|i| i + 1}
end
end

I blieve the yield forwarding is slower because there is one more call.
Wouldn't the &block form be more clutter? Well, not that it's so
much clutter in either case, but I think it would be slightly wordier
(def five_time(&block) etc.)

For forwarding I prefer &b because the original block is simply passed on
vs. a new block yields to the original block. That seems awkward to me
and seems to be slower, too. Personally I find the yield forwarding more
clutter. YMMV

Kind regards

robert
 
D

David A. Black

Hi --

13:21:36 [robert.klemme]: ruby yield-vs-block.rb
user system total real
yield 0.250000 0.000000 0.250000 ( 0.249000)
block 0.125000 0.000000 0.125000 ( 0.124000)
13:21:44 [robert.klemme]: cat yield-vs-block.rb

require 'benchmark'
include Benchmark

REP = 100000

def bl_yield(n)
n.times {|i| yield i}
end

def bl_fwd(n,&b)
n.times(&b)
end

Those aren't equivalent, though. The second one would have to be:

n.times {|i| b.call(i) }

to compare the two idioms directly.

When I make that change I get:

yield 0.320000 0.000000 0.320000 ( 0.332194)
block 0.460000 0.000000 0.460000 ( 0.490299)

I blieve the yield forwarding is slower because there is one more call.


For forwarding I prefer &b because the original block is simply passed on
vs. a new block yields to the original block. That seems awkward to me
and seems to be slower, too. Personally I find the yield forwarding more
clutter. YMMV

The clutter point was (at least in my mind :) strictly about the
difference between:

def x
yield
end

and

def x(&b)
b.call
end

As for forwarding a block to a new method -- isn't &b the only way?
Or am I behind the times on automatic propagation? (But even in that
case I don't think supplying block #1 to method #2 is the same as
yielding to block #1 during method #1.)


David
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top