Blocks and Closures

A

Alex McHale

If this is covered elsewhere, please point me in the right direction.

Either why, my question is what exactly is the difference between a
block / closure of the syntax
do |x, y|
....
end

versus

{|x, y|
....
}

Are these idioms identical in all but their syntax? Or is there an
underlying difference to them?

This has been bugging me for a while, and I haven't been able to
locate the answer in the docs I've found.

Thanks!

Alex McHale
 
J

Joel VanderWerf

Alex said:
If this is covered elsewhere, please point me in the right direction.

Either why, my question is what exactly is the difference between a
block / closure of the syntax
do |x, y|
....
end

versus

{|x, y|
....
}

Are these idioms identical in all but their syntax? Or is there an
underlying difference to them?

This has been bugging me for a while, and I haven't been able to
locate the answer in the docs I've found.

The only difference is precedence:

def foo(*args, &bl)
puts "foo got the block" if bl
end

def bar(*args, &bl)
puts "bar got the block" if bl
end

foo bar {} # ==> bar got the block
foo bar do end # ==> foo got the block
 
A

Alex McHale

Alright, while that is very confusing to me (not the concept, I
understand what you said, just why the precedence is different), you
raise another question.

In your example, you use the &bl syntax. I understand that if you
want the proc object, that is the syntax to use, but why use it in
that case?

Wouldn't this give identical results?

def foo(*args)
puts "foo got the block" if yield
end

etc? Is the reason to use this construct because you want to demand
that a block be passed?

And on that note, is there a syntax to determine whether or a block
was passed to the method, in the syntax of my construct?

Thanks!
 
J

Joel VanderWerf

Alex said:
Alright, while that is very confusing to me (not the concept, I
understand what you said, just why the precedence is different), you
raise another question.

In your example, you use the &bl syntax. I understand that if you
want the proc object, that is the syntax to use, but why use it in
that case?

Wouldn't this give identical results?

def foo(*args)
puts "foo got the block" if yield
end

etc? Is the reason to use this construct because you want to demand
that a block be passed?

And on that note, is there a syntax to determine whether or a block
was passed to the method, in the syntax of my construct?

You're right, there's no reason to use the &bl notation. You can use the
Kernel#block_given? method:

def foo(*args)
puts "foo got the block" if block_given?
end

But using 'if yield' is different: it tests the return value of the
block, not the existence of the block.
 
F

Florian Gross

Alex said:
Are these idioms ["foo {...}" and "foo do ... end"] identical in all
but their syntax? Or is there an underlying difference to them?

Mostly. There's only a small difference in how they bind to methods:
irb(main):001:0> def foo(*args, &block); puts "foo got block" if block; end
irb(main):002:0> def bar(*args, &block); puts "bar got block" if block; end
irb(main):003:0> foo bar {}
bar got block
irb(main):004:0> foo bar do end
foo got block

Hope this helps. :)

No problem.

Regards,
Florian Gross
 
A

Alex McHale

Oooh I understand now. In fact I had given my own answer initially,
by noting that bl is the proc object.

Thank you for the clarification. I come to love Ruby more and more
every day ...

For me, Ruby is a lot like vim. I've been using it for years, and I
still learn something new about it every day.
 
G

Gavin Sinclair

You're right, there's no reason to use the &bl notation. You can use the
Kernel#block_given? method:


Just a note for the OP. The following example shows the advantage of
the &block notation in certain circumstances.

def bar
return 5 + yield
end

def foo(&block)
bar(block)
end

'foo' is now a proxy for 'bar'.

Gavin
 
J

Joel VanderWerf

Gavin said:
Just a note for the OP. The following example shows the advantage of
the &block notation in certain circumstances.

def bar
return 5 + yield
end

def foo(&block)
bar(block)
end

'foo' is now a proxy for 'bar'.

Slight nit: 'bar(block)' should be 'bar(&block)', if you want to
propagate the block.

Actually, though, there is a more efficient way to propagate a block:

def bar
return 5 + yield
end

def foo
bar {yield}
end

p foo {3} # ==> 8

I guess it's more efficient because no Proc object is created. This
technique doesn't work if you need to store the block somewhere for
later access. Then you really do need to use & or Proc.new.

Here's some benchmarking (ruby-1.9.0):

require 'benchmark'

def outer11(&bl)
inner1(&bl)
end

def outer12(&bl)
inner2(&bl)
end

def outer21
inner1 {yield}
end

def outer22
inner2 {yield}
end

def inner1(&bl)
bl.call
end

def inner2
yield
end

n = 100000

Benchmark.bmbm(10) do |rpt|
rpt.report("outer11") do
n.times {outer11{}}
end

rpt.report("outer12") do
n.times {outer12{}}
end

rpt.report("outer21") do
n.times {outer21{}}
end

rpt.report("outer22") do
n.times {outer22{}}
end
end

__END__

Output:

Rehearsal ---------------------------------------------
outer11 1.850000 0.010000 1.860000 ( 1.856130)
outer12 1.600000 0.000000 1.600000 ( 1.600134)
outer21 2.210000 0.000000 2.210000 ( 2.214836)
outer22 0.520000 0.000000 0.520000 ( 0.526088)
------------------------------------ total: 6.190000sec

user system total real
outer11 1.870000 0.000000 1.870000 ( 1.871797)
outer12 1.620000 0.000000 1.620000 ( 1.623072)
outer21 2.230000 0.000000 2.230000 ( 2.236854)
outer22 0.500000 0.000000 0.500000 ( 0.501914)
 

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,774
Messages
2,569,596
Members
45,141
Latest member
BlissKeto
Top