Reputation: 313
so I'm trying this code out,
class Colors
def initialize color
color.each {|c| color c}
end
def color c
self.class.send(:define_method, "about_#{c}") do
puts "The color #{c} has an intensity of #{Random.rand}"
end
end
end
a = Colors.new(["orange", "pink", "yellow", "green"])
a.about_pink
a.about_pink
a.about_pink
On my machine I get:
The color pink has an intensity of 0.377090691263002
The color pink has an intensity of 0.8375972769713161
The color pink has an intensity of 0.26820920750202837
the problem is that 4 statements each with a different number are printed. Shouldn't all the statements printed contain the same random number as the method is "defined" only once?
Upvotes: 1
Views: 99
Reputation: 168101
No. The timing in which a block is evaluated is determined by the method that uses it. Some methods (such as tap
) evaluate it immediately, some others (such as [].each
) would never evaluate it. In your case, send
does not do anything to the block, and passes it to define_method
, which turns the block into the method body of the method being defined. That means that the block would be evaluated every time the defined method is called.
Upvotes: 1
Reputation: 2864
What you want to do, is to evaluate Random when you're defining the method. That way, the value is fixed for the defined method:
class Colors
def initialize color
@int = {}
color.each {|c| color c}
end
def color c
intensity = Random.rand
self.class.send(:define_method, "about_#{c}") do
puts "The color #{c} has an intensity of #{intensity}"
end
end
end
a = Colors.new(["orange", "pink", "yellow", "green"])
a.about_pink
a.about_pink
a.about_pink
As you can see, I save the result of Random in a variable, which is fixed in the internal context. What happens in your initial example, is that the string that you output gets evaluated in every call, and that evaluation has the call to Random
inside of it, so it runs it every time.
Upvotes: 3
Reputation: 16507
Since you always do a call to Random.rand
inside your block, the value always will be different because the block code is also always called. So you result is correct. But to keep the value at first time, and reuse it, just store it to a var as follows:
def initialize color
@int = {}
color.each {|c| color c}
end
def color c
self.class.send(:define_method, "about_#{c}") do
puts "The color #{c} has an intensity of #{@int[ c ] ||= Random.rand}"
end
end
And we will get something like:
The color pink has an intensity of 0.6879417562602181
The color pink has an intensity of 0.6879417562602181
The color pink has an intensity of 0.6879417562602181
The color orange has an intensity of 0.8019817000526268
Upvotes: 1