Using yield

M

Mark Hubbart

As for forwarding a block to a new method -- isn't &b the only way?
Or am I behind the times on automatic propagation?

I forward blocks a lot in my own code, and wish there was a way to get
around using the &block syntax. I suspect that someone else has
probably suggested this already, but what about a &yield keyword? ie.:

def thrice
3.times &yield
end

Assuming it would be optimized, and wouldn't create a Proc object,
this could be a nicer way of forwarding a block.

cheers,
Mark
 
R

Robert Klemme

David A. Black said:
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.

Hm, I'm not sure I agree here. My point was to compare performance of two
idioms achieving that a block handed to a method is called from another
method called within that method. You are right with respect to the
invocation chain though.
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)



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?

Strictly speaking yes. But the idiom presented in my bl_yield() achieves
about the same. At least there are no semantic differences I'm aware of.
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.)

What exactly do you think is the difference here? (Apart from performance
of course)

Kind regards

robert
 
D

David A. Black

Hi --

David A. Black said:
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.

Hm, I'm not sure I agree here. My point was to compare performance of two
idioms achieving that a block handed to a method is called from another
method called within that method. You are right with respect to the
invocation chain though.

I think we're talking about different sets of "two idioms", but I see
what you mean about your set. As for....
What exactly do you think is the difference here? (Apart from performance
of course)

... there's something nagging in my mind that I can't quite nail down,
having something to do with argument parsing, I think. It's probably
illusory. If I manage to figure out what it is, I'll post it :)


David
 
R

Robert Klemme

David A. Black said:
Hi --

David A. Black said:
Hi --

On Tue, 7 Dec 2004, Robert Klemme wrote:

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.

Hm, I'm not sure I agree here. My point was to compare performance of two
idioms achieving that a block handed to a method is called from another
method called within that method. You are right with respect to the
invocation chain though.

I think we're talking about different sets of "two idioms",

Yes, we are / were. :)
but I see
what you mean about your set. As for....


.. there's something nagging in my mind that I can't quite nail down,
having something to do with argument parsing, I think. It's probably
illusory. If I manage to figure out what it is, I'll post it :)

Thanks! I'm curios to learn what you find out. Wait...

If the other method (Fixnum#times in my example) would do different
yielding. Hm... I think I should redefine my set:

14:52:25 [09_Public]: ruby yield-vs-block.rb
user system total real
yield 0.265000 0.000000 0.265000 ( 0.263000)
yield_star 0.766000 0.000000 0.766000 ( 0.778000)
block 0.109000 0.000000 0.109000 ( 0.124000)
14:52:58 [09_Public]: cat yield-vs-block.rb

require 'benchmark'
include Benchmark

REP = 100000

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

def bl_yield_star(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("yield_star") do
bl_yield_star(REP) {|i| i + 1}
end

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

Uh, even worse performance for "yield *i". *shudder* But not really
unexpected. One more reason to use &b for forwarding.

Kind regards

robert
 
D

David A. Black

If the other method (Fixnum#times in my example) would do different
yielding. Hm... I think I should redefine my set:

14:52:25 [09_Public]: ruby yield-vs-block.rb
user system total real
yield 0.265000 0.000000 0.265000 ( 0.263000)
yield_star 0.766000 0.000000 0.766000 ( 0.778000)
block 0.109000 0.000000 0.109000 ( 0.124000)
14:52:58 [09_Public]: cat yield-vs-block.rb

require 'benchmark'
include Benchmark

REP = 100000

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

def bl_yield_star(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("yield_star") do
bl_yield_star(REP) {|i| i + 1}
end

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

Uh, even worse performance for "yield *i". *shudder* But not really
unexpected. One more reason to use &b for forwarding.

I think my vague concern about argument parsing has to do with the
question of who gets to decide. There might be a case where for some
reason you wanted to do:

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

or something weird like that... but I guess that takes us away from
exact equivalence with &b, since now I'm not just passing the args to
yield one at a time as they come.


David
 
R

Robert Klemme

David A. Black said:
If the other method (Fixnum#times in my example) would do different
yielding. Hm... I think I should redefine my set:

14:52:25 [09_Public]: ruby yield-vs-block.rb
user system total real
yield 0.265000 0.000000 0.265000 ( 0.263000)
yield_star 0.766000 0.000000 0.766000 ( 0.778000)
block 0.109000 0.000000 0.109000 ( 0.124000)
14:52:58 [09_Public]: cat yield-vs-block.rb

require 'benchmark'
include Benchmark

REP = 100000

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

def bl_yield_star(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("yield_star") do
bl_yield_star(REP) {|i| i + 1}
end

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

Uh, even worse performance for "yield *i". *shudder* But not really
unexpected. One more reason to use &b for forwarding.

I think my vague concern about argument parsing has to do with the
question of who gets to decide. There might be a case where for some
reason you wanted to do:

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

or something weird like that... but I guess that takes us away from
exact equivalence with &b, since now I'm not just passing the args to
yield one at a time as they come.

Yeah, the &equivalent would be

def some_method(n,&b)
n.times {|*i| b.call(i)}
end

which suffers a similar detour as the yield approach. I think the most
common case for "block forwarding" does not involve argument mangling so
this is probably a special case.

Kind regards

robert
 

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

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top