1

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?

3 Answers 3

3

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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

1 Comment

what I meant to ask was why is value changing in the first place? The methods are defined when initialize is called. Shouldn't the number then stay constant for each method call? Are the methods being redefined for each call?
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.