Forum: Ruby define_method fun

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
E4dd2cacb0207907f1961d1a76e14141?d=identicon&s=25 Patrick Ritchie (Guest)
on 2006-02-18 04:33
(Received via mailing list)
Hi All,

I just setup the following in one of my classes, I'll let the code speak
for itself:

["<", "<=", ">", ">=", "==", "<=>"].each do |op|
  define_method(op) { |comparison| eval "#{self.order} #{op}
#{comparison.order}" }
end

Ruby continues to impress me with it's ability to do so much in so few
amazing readable lines of code.

Are their any good reasons not to declare my op methods this way? Is
their an even more efficient way to achieve the same effect?

Cheers!
Patrick
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-02-18 06:52
(Received via mailing list)
Patrick Ritchie wrote:
> Ruby continues to impress me with it's ability to do so much in so few
> amazing readable lines of code.
>
> Are their any good reasons not to declare my op methods this way? Is
> their an even more efficient way to achieve the same effect?

It helps to pull the eval out of the define_method:


module Order
  attr_reader :order
  def initialize order
    @order = order
  end
end

class EvalInside
  include Order
  ["<", "<=", ">", ">=", "==", "<=>"].each do |op|
    define_method(op) { |comparison| eval "#{self.order} #{op}
#{comparison.order}" }
  end
end

class EvalOutside
  include Order
  ["<", "<=", ">", ">=", "==", "<=>"].each do |op|
    eval "define_method(:#{op}) { |comparison| self.order #{op}
comparison.order }"
  end
end

require 'benchmark'

inside = []
outside = []
1000000.times do |i|
  order = rand(1)
  inside << EvalInside.new(i)
  outside << EvalOutside.new(i)
end

Benchmark.bmbm do |bm|
  bm.report("eval inside") do
    inside.sort
  end

  bm.report("eval outside") do
    outside.sort
  end
end


Rehearsal ------------------------------------------------
eval inside   17.280000   0.030000  17.310000 ( 18.088599)
eval outside   2.170000   0.010000   2.180000 (  2.254999)
-------------------------------------- total: 19.490000sec

                   user     system      total        real
eval inside   17.260000   0.010000  17.270000 ( 17.873053)
eval outside   2.150000   0.010000   2.160000 (  2.248230)
230ccd9aa66613ac4515ff5861b4c5af?d=identicon&s=25 Gabe Boyer (Guest)
on 2006-02-18 06:55
(Received via mailing list)
Take a look at the Comparable[1] module. It's a much cleaner, simpler
way to accomplish what you're doing.

[1] http://www.ruby-doc.org/core/classes/Comparable.html

Gabe
E4dd2cacb0207907f1961d1a76e14141?d=identicon&s=25 Patrick Ritchie (Guest)
on 2006-02-18 15:51
(Received via mailing list)
Wow, that's impressive, why such huge difference?

Cheers!
Patrick
E4dd2cacb0207907f1961d1a76e14141?d=identicon&s=25 Patrick Ritchie (Guest)
on 2006-02-18 15:55
(Received via mailing list)
Definitely cleaner in my case, thanks for the tip!

Cheers!
Patrick
18ca239ffade6df0b839d26062f173fb?d=identicon&s=25 Dominik Bathon (Guest)
on 2006-02-18 17:16
(Received via mailing list)
On Sat, 18 Feb 2006 06:49:37 +0100, Joel VanderWerf
<vjoel@path.berkeley.edu> wrote:

>>
>> Ruby continues to impress me with it's ability to do so much in so few
>> amazing readable lines of code.
>>
>> Are their any good reasons not to declare my op methods this way? Is
>> their an even more efficient way to achieve the same effect?
>
> It helps to pull the eval out of the define_method:

Or using no eval at all:

>     define_method(op) { |comparison| eval "#{self.order} #{op}
> end
class NoEval
   include Order
   [:<, :<=, :>, :>=, :==, :<=>].each do |op|
     define_method(op) { |comparison| self.order.send(op,
comparison.order)
}
   end
end

> require 'benchmark'
>
> inside = []
> outside = []
noeval = []
400000.times do |i|
>   order = rand(1)
>   inside << EvalInside.new(i)
>   outside << EvalOutside.new(i)
   noeval << NoEval.new(i)
> end
>
> Benchmark.bmbm do |bm|
>   bm.report("eval inside") do
>     inside.sort
>   end
>
>   bm.report("eval outside") do
>     outside.sort
>   end

   bm.report("no eval") do
     noeval.sort
   end
> end

Rehearsal ------------------------------------------------
eval inside   19.310000   0.060000  19.370000 ( 19.500018)
eval outside   1.940000   0.010000   1.950000 (  1.951829)
no eval        2.600000   0.010000   2.610000 (  2.626695)
-------------------------------------- total: 23.930000sec

                    user     system      total        real
eval inside   18.690000   0.080000  18.770000 ( 18.950481)
eval outside   1.920000   0.000000   1.920000 (  1.940485)
no eval        2.010000   0.000000   2.010000 (  2.028688)


Dominik
912c61d9da47754de7039f4271334a9f?d=identicon&s=25 MenTaLguY (Guest)
on 2006-02-18 19:51
(Received via mailing list)
On Sat, 2006-02-18 at 23:51 +0900, Patrick Ritchie wrote:
> Wow, that's impressive, why such huge difference?

eval-ing (especially a string) is expensive, basically.

If you move the eval outside the method body, it means you only pay that
cost once (when defining the method), rather than every time the method
is run.

-mental
58479f76374a3ba3c69b9804163f39f4?d=identicon&s=25 Eric Hodel (Guest)
on 2006-02-18 21:25
(Received via mailing list)
On Feb 17, 2006, at 7:32 PM, Patrick Ritchie wrote:

> Ruby continues to impress me with it's ability to do so much in so
> few amazing readable lines of code.
>
> Are their any good reasons not to declare my op methods this way?
> Is their an even more efficient way to achieve the same effect?

class YourClass

   include Comparable

   def <=>(other)
     self.order <=> other.order
   end

end

--
Eric Hodel - drbrain@segment7.net - http://segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com
E4dd2cacb0207907f1961d1a76e14141?d=identicon&s=25 Patrick Ritchie (Guest)
on 2006-02-21 14:58
(Received via mailing list)
Ah ah, makes total sense now thanks for the explanation.

Cheers!
Patrick
This topic is locked and can not be replied to.