Define_method fun


#1

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


#2

Patrick R. 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)


#3

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


#4

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

Cheers!
Patrick


#5

Definitely cleaner in my case, thanks for the tip!

Cheers!
Patrick


#6

On Sat, 18 Feb 2006 06:49:37 +0100, Joel VanderWerf
removed_email_address@domain.invalid 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


#7

On Feb 17, 2006, at 7:32 PM, Patrick R. 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 H. - removed_email_address@domain.invalid - http://segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com


#8

On Sat, 2006-02-18 at 23:51 +0900, Patrick R. 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


#9

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

Cheers!
Patrick