Define_method fun

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

Patrick Ritchie wrote:

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?

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)

···

--
      vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

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

···

On 2/17/06, Patrick Ritchie <pritchie@videotron.ca> wrote:

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

class YourClass

   include Comparable

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

end

···

On Feb 17, 2006, at 7:32 PM, Patrick Ritchie wrote:

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?

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

http://trackmap.robotcoop.com

Wow, that's impressive, why such huge difference?

Cheers!
Patrick

···

Patrick Ritchie wrote:

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

Definitely cleaner in my case, thanks for the tip!

Cheers!
Patrick

···

Take a look at the Comparable[1] module. It's a much cleaner, simpler
way to accomplish what you're doing.

[1] module Comparable - RDoc Documentation

Gabe

On 2/17/06, Patrick Ritchie <pritchie@videotron.ca> wrote:

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

Patrick Ritchie wrote:

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?

It helps to pull the eval out of the define_method:

Or using no eval at all:

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

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

···

On Sat, 18 Feb 2006 06:49:37 +0100, Joel VanderWerf <vjoel@path.berkeley.edu> wrote:

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

···

On Sat, 2006-02-18 at 23:51 +0900, Patrick Ritchie wrote:

Wow, that's impressive, why such huge difference?

Ah ah, makes total sense now thanks for the explanation.

Cheers!
Patrick

···

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