Creating independent lambdas in loops

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

a =

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

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

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

Guy Decoux

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

···

On Wed, 28 Sep 2005, Michael Roth wrote:

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()?

--

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

===============================================================================

"ts" <decoux@moulon.inra.fr> wrote in message
news:200509281241.j8SCflhq021177@moulon.inra.fr...

> a =

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

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

Why the difference in behavior between these two?

ts wrote:

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

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

> a =

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

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

···

--- ts <decoux@moulon.inra.fr> wrote:

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

__________________________________
Yahoo! Mail - PC Magazine Editors' Choice 2005

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

Michael Roth wrote:

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

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

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.

···

On 9/28/05, Eric Mahurin <eric_mahurin@yahoo.com> wrote:

--- ts <decoux@moulon.inra.fr> wrote:

>

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.

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