So, I'm trying to hack at the math module.
Expected:
>> Math.sqrt(2)
=> sqrt(2)
Actual result, mileage does not vary:
>> Math.sqrt(2)
=> 1.4142135623731
require 'mathn'
module Math
alias :old_sqrt :sqrt
def sqrt x
result = old_sqrt x
if result.is_a? Float
"sqrt(#{x})"
else
result
end
end
end
puts Math.sqrt(2)
I had originally not attempted an alias, I just did "result = super x"
but it didn't really amount to much, either.
Where am I thinking about this wrong?
--
Posted via http://www.ruby-forum.com/.
Looks fine to me. Does old_sqrt actually return a Float or some other
numeric type?
Actually, why use type checking at all? Why not change the condition to
use kind_of or simply test result.to_i - result == 0 ?
Best,
--
Marnen Laibow-Koser
http://www.marnen.org
mar...@marnen.org
The problem is that mathn is using the module_function method to
convert sqrt into an method of the Math module.
According to http://ruby-doc.org/core/classes/Module.src/M001642.html:
"Module functions are copies of the original, and so may be changed
independently"
So I think you are not redefining the module function, but the
original, which doesn't have any effect when you call Math.sqrt (this
is calling the version created by module_function). Doing this:
irb(main):042:0> module Math
irb(main):043:1> def sqrt x
irb(main):044:2> result = super x
irb(main):045:2> p [result, result.class]
irb(main):046:2> result
irb(main):047:2> end
irb(main):048:1> module_function :sqrt
irb(main):049:1> end
=> Math
irb(main):050:0> Math.sqrt 2
NoMethodError: super: no superclass method `sqrt'
from (irb):44:in `sqr
allows you to actually override the version created by
module_function, but I don't know how to then call the original, since
neither the original alias you had nor super are working. But maybe
this points you in the right direction.
Jesus.
Got it:
irb(main):001:0> require 'mathn'
=> true
irb(main):002:0> module Math
irb(main):003:1> class << self
irb(main):004:2> alias :old_sqrt :sqrt
irb(main):005:2> end
irb(main):006:1> def sqrt x
irb(main):007:2> result = old_sqrt x
irb(main):008:2> p [result, result.class]
irb(main):009:2> result
irb(main):010:2> end
irb(main):011:1> module_function :sqrt
irb(main):012:1> end
=> Math
irb(main):013:0> Math.sqrt 2
[1.4142135623731, Float]
=> 1.4142135623731
Jesus.
require 'benchamrk'
num = 5.5
n = 5_000_000
Benchmark.bmbm do |x|
x.report("kind_of?") { n.times do ; n.kind_of? Float ; end }
x.report("is_a?") { n.times do ; n.is_a? Float ; end }
x.report("to_i") { n.times do ; n.to_i - n == 0 ; end }
end
Rehearsal --------------------------------------------
kind_of? 1.719000 0.000000 1.719000 ( 1.720000)
is_a? 1.641000 0.000000 1.641000 ( 1.642000)
to_i 2.750000 0.000000 2.750000 ( 2.751000)
----------------------------------- total: 6.110000sec
user system total real
kind_of? 1.734000 0.000000 1.734000 ( 1.735000)
is_a? 1.703000 0.000000 1.703000 ( 1.720000)
to_i 2.688000 0.000000 2.688000 ( 2.689000)
__________________
You know how metaprogramming is all about the self, according to Yehuda
Katz's latest blog post? :) I, er, forgot to do self.sqrt ...
module Math
alias_method :old_sqrt, :sqrt
def self.sqrt x
result = 5.5 #self.old_sqrt(x)
if result.is_a? Float
"sqrt(#{x})"
else
result
end
end
end
>> puts Math.sqrt(2)
=> sqrt(2)
>> puts Math.methods.find { |i| i[0..0] == "o"}
=> object_id
.. So I'm not sure how to do an alias_method on a method that's got a
"self." in front of it, I guess..
puts Math.methods.find { |i| i[0..0] == "o"}
> irb(main):003:1> class << self
> irb(main):004:2> alias :old_sqrt :sqrt
> irb(main):005:2> end
Ah-ha! My version is underneath. So, what the bit of code I quoted does
is.. It reopens "the class of the module" to make a change in the alias?
module Math
class << self
alias :old_sqrt :sqrt
end
def self.sqrt x
result = old_sqrt(x)
if result.is_a? Float
"sqrt(#{x})"
else
result
end
end
end
>> puts Math.sqrt(2)
=> sqrt(2)
Yes, module_function creates a method in the singleton class of the module.
So you need to enter that singleton class via class << self to be in a
place where self is that singleton class, in order to be able to alias
that method correctly.
>
> module Math
>
> class << self
> alias :old_sqrt :sqrt
> end
>
> def self.sqrt x
> result = old_sqrt(x)
> if result.is_a? Float
> "sqrt(#{x})"
> else
> result
> end
> end
> end
>
>
>>> puts Math.sqrt(2)
> => sqrt(2)
Jesus.
Good time to google the difference if you don't already know.
Julian
Blog: http://random8.zenunit.com/
Twitter: http://twitter.com/random8r
Learn: http://sensei.zenunit.com/
New video up now at http://sensei.zenunit.com/ real fastcgi rails
deploy process! Check it out now!
Is this a new insight, or is this just putting words to what Jesus
already helped me discover (and hack) ?
Instance method : Customer.new.phone # (probably yields an error : phone
number not set)
Class method : Customer.find(:first) # (Rails-style)
Right ?
Well, people usually call class methods to methods that are defined in
the singleton class of a class.
In this case it could be a bit confusing, since we are not talking
about a class but a Module.
So I would call it a module method (or module function).
> Instance method : Customer.new.phone # (probably yields an error : phone
> number not set)
> Class method : Customer.find(:first) # (Rails-style)
>
> Right ?
Right, but it would be more clear to say module method, in my opinion.
Jesus.
Math.sqrt(5) : Module method, because it's defined in a module
Customer.find(:first) : Class method, because it's defined in the class
Right? :)