Unespected result of define_method with "for" and "each"

L

lucapette

Hi everyone,

I was trying to define instance methods on a class based on an given array of strings. I run into different results using two different types of iteration. I can't understand the resulting behaviour just in a case, the following piece of code will be more meaningful than words:

class Toy

end

%w{e1 e2}.each { |m|
Toy.class_eval do
define_method m do
puts m
end
end
}

for m in %w{u1 u2}
Toy.class_eval do
define_method m do
puts m
end
end
end

p=Toy.new

puts p.e1 # ok
puts p.e2 # ok
puts p.u1 # WTF
puts p.u2 # ok

I tested it with ruby 1.8.7 and with 1.9.2 and I get the same results . Obviously, the third result is the only I don't understand :). It seems that define_method defers the evaluation of "m" object when used with a for.

I'm relatively new to Ruby and maybe this is the expected behaviour. I would like to understand the difference between the to version, thank you.

P.S. I added this example as a gist, you can read it here https://gist.github.com/868310
 
J

Jesús Gabriel y Galán

Hi everyone,

I was trying to define instance methods on a class based on an given arra=
y of strings. I run into different results using two different types of ite=
ration. I can't understand the resulting behaviour just in a case, the foll=
owing piece of code will be more meaningful than words:
class Toy

end

%w{e1 e2}.each { |m|
=A0 =A0Toy.class_eval do
=A0 =A0 =A0 =A0define_method m do
=A0 =A0 =A0 =A0 =A0 =A0puts m
=A0 =A0 =A0 =A0end
=A0 =A0end
}

for m in %w{u1 u2}
=A0 =A0Toy.class_eval do
=A0 =A0 =A0 =A0define_method m do
=A0 =A0 =A0 =A0 =A0 =A0puts m
=A0 =A0 =A0 =A0end
=A0 =A0end
end

p=3DToy.new

puts p.e1 # ok
puts p.e2 # ok
puts p.u1 # WTF
puts p.u2 # ok

I tested it with ruby 1.8.7 and with 1.9.2 and I get the same results . O=
bviously, the third result is the only I don't understand :). It seems that=
define_method defers the evaluation of "m" object when used with a for.
I'm relatively new to Ruby and =A0maybe this is the expected behaviour. I=
would like to understand the difference between the to version,
thank you.

It is expected, and the reason is the way closures work and the fact
that the block passed to "each" is a new scope, while the body of the
"for" is not a new scope. In the first case, the closures created by
class_eval and define_method close over a local variable m, *which is
different for each iteration*, because it is local to the block passed
to each, and the block is a new scope, so every iteration creates a
new variable m. In the second case, though, the m is a local variable
but to the whole script, it's not local to the body of the for, since
the body of the for does not create a new scope. And so, in this case
the closures created by class_eval and define_method close over a
single variable m, which after all is said and done refers to the
string "u2", so when you are calling the method u1, it returns this
string.

Jesus.
 

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,968
Messages
2,570,154
Members
46,702
Latest member
LukasConde

Latest Threads

Top