creating independent lambdas in loops

M

Michael Roth

Hello all,

I'm still making progress in learning ruby, but I ran across a problem
with lambda in loops wich gives me real headache until I found it. My
code looked like this:


a = []

for multi in [2, 3, 4, 5]
a << lambda do |n|
n * multi
end
end

for mul in a
puts mul.call(17)
end


I thought, this code will output four numbers 34, 51, 68, 85 but it
doesn't...
Instead it printed four times '85'. Obviously the four created blocks
are all using the same instance of 'multi'.

However, if I rewrite these lines to the following, it works as expected:


def create_mul multi
lambda do |n|
n * multi
end
end

b = []

for multi in [2, 3, 4, 5]
b << create_mul(multi)
end

for mul in b
puts mul.call(17)
end


So my question is: If there is an easy way to get the expected behavior
without a helper function like create_mul()?

In my app the lambdas to create in the loop are really small and I don't
would like to put their code outside the loop by reason of clarity.


Michael Roth
 
T

ts

M> a = []

M> for multi in [2, 3, 4, 5]
M> a << lambda do |n|
M> n * multi
M> end
M> end

[2, 3, 4, 5].each do |multi|
a << lambda do |n|
n * multi
end
end

M> for mul in a
M> puts mul.call(17)
M> end


Guy Decoux
 
M

Michael Roth

Michael said:
Already tried. Doesn't work... :-\

Argh, stop. Looks like it works. But only if 'multi' doesn't appears
somewhere before the each-loop.
 
T

ts

M> Already tried. Doesn't work... :-\


moulon% cat b.rb
#!/usr/bin/ruby
a = []
[2, 3, 4, 5].each do |multi|
a << lambda do |n|
n * multi
end
end
for mul in a
puts mul.call(17)
end
moulon%

moulon% ./b.rb
34
51
68
85
moulon%


Guy Decoux
 
E

Eric Mahurin

--- ts said:
=20
M> a =3D []
=20
M> for multi in [2, 3, 4, 5]
M> a << lambda do |n|
M> n * multi
M> end
M> end
=20
[2, 3, 4, 5].each do |multi|
a << lambda do |n|
n * multi
end
end

The each loop works because the variable multi is local to the
each block. Each iteration through that block will use a
different variable multi.

To me, it seems like a bug that the for loop isn't equivalent
to the each loop. I think it is putting multi in the
surrounding scope because of the syntactic order - multi comes
before the Enumerable.

You'll see the same problem if you assign multi before the each
loop. If you wanted to continue using the for loop (or multi
was already used), you could make a localized version of it
like this:

a =3D []
for multi in [2, 3, 4, 5]
lambda do |multi_local|
a << lambda do |n|
n * multi_local
end
end.call(multi)
end
for mul in a
puts mul.call(17)
end

I didn't realize that within a block, the only variables truly
local are the argument variables. I think I've read somewhere
that in the future ruby will put better facilities in for
controlling variable scope locality.
M> for mul in a
M> puts mul.call(17)
M> end



=09
__________________________________=20
Yahoo! Mail - PC Magazine Editors' Choice 2005=20
http://mail.yahoo.com
 
J

Jim Freeze

To me, it seems like a bug that the for loop isn't equivalent
to the each loop. I think it is putting multi in the
surrounding scope because of the syntactic order - multi comes
before the Enumerable.

It's an alternate form that has different scoping, similar to {} and do/end
which have different precedence.

I wouldn't consider it a bug. I would just consider it a very good reason
to never use a 'for' loop in a Ruby program. :)

--
Jim Freeze
WARNING: The Ruby populace has determined that using 'for'
loops in a Ruby script may be harmful to your health. You should
consult a pure Rubyist (who doesn't have ties to Perl or C) before
using this feature.
 
A

Ara.T.Howard

Hello all,

I'm still making progress in learning ruby, but I ran across a problem
with lambda in loops wich gives me real headache until I found it. My
code looked like this:


a = []

for multi in [2, 3, 4, 5]
a << lambda do |n|
n * multi
end
end

for mul in a
puts mul.call(17)
end


I thought, this code will output four numbers 34, 51, 68, 85 but it
doesn't...
Instead it printed four times '85'. Obviously the four created blocks
are all using the same instance of 'multi'.

However, if I rewrite these lines to the following, it works as expected:


def create_mul multi
lambda do |n|
n * multi
end
end

b = []

for multi in [2, 3, 4, 5]
b << create_mul(multi)
end

for mul in b
puts mul.call(17)
end


So my question is: If there is an easy way to get the expected behavior
without a helper function like create_mul()?

you don't have to write a __specific__ helper method - you can have a general
purpose one to help with cases like these:

harp:~ > cat a.rb
def scope *a; yield *a; end

a = []

for multi in [2, 3, 4, 5]
a << scope(multi){|m| lambda{|n| n * m} }
end

for mul in a
puts mul.call(17)
end


harp:~ > ruby a.rb
34
51
68
85

or go golfing

harp:~ > cat a.rb
puts (2 .. 5).map{|m| lambda{|n| n * m} }.map{|l| l.call 17}

harp:~ > ruby a.rb
34
51
68
85

cheers.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| Your life dwells amoung the causes of death
| Like a lamp standing in a strong breeze. --Nagarjuna
===============================================================================
 

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,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top