rational - original numbers in ruby

2019-02-19 14:22发布

问题:

How do I get the original numbers? For example when I type:

r = Rational(2, 10)
# (1/5)

2 and 10 will be changed to 1 and 5:

r.numerator   # 1
r.denominator # 5

How do I get 2 & 10 from instance of Rational class(r)?

I monkey-patched Rational class and created new method(Rational_o):

def Rational_o *args
  x, y = args
  r = Rational *args
  r.x = x
  r.y = y
  r
end

class Rational
  attr_accessor :x, :y
end

It works, but is there build-in method or variable(s) where original x & y are stored?

回答1:

No, there isn't. Reduction is a basic and common way to normalize rational numbers. Why would a rational number keep the original numerator and denominator? It does not make sense.

Your question is like asking "Does a string created by "foo" + "bar" (which becomes "foobar") keep the original substrings "foo" and "bar"? Where are they stored?"

If you really want to keep the original numbers, then a rational number is not what you want, and subclassing Rational is not the right way to go. You should use an array holding a pair of numbers.



回答2:

Rational numbers get normalized on initialization, so you can not know which numbers where given as original arguments. You can also not subclass Rational to install an initializer of your own, and monkeypatching as you do is not really the optimal way to achieve what you want to achieve (which I think you know).

What you can do is create a proxy around Rational with BasicObject which preserves the original arguments and will not disturb normal operation of the original Rational class

class RationalWithArgumentStore < BasicObject
  attr_accessor :original_arguments, :rational

  def initialize *args
    @original_arguments = args
    @rational = Rational *args
  end

  # Basic Object is a basic object, but defines ==
  # so let's overwrite it to route to rational
  def == other
    @rational == other
  end

  # Route all unknown method calls to rational
  def method_missing meth, *args, &block
    @rational.send meth, *args, &block
  end

end

def RationalWithArgumentStore(*args)
  RationalWithArgumentStore.new(*args)
end

So now you can do

my_rational = RationalWithArgumentStore(2,10)
my_rational.original_arguments #=> [2, 10]

#use it like a normal rational
my_rational * 3
my_rational.truncate


回答3:

No, there is no such inbuilt private or public method that does what you want to do.

If you really want to store the original numbers inside the instance method, your monkey-patch is definitely one of the ways to do so.

In fact, the method Rational(a,b) is an instance method defined outside the class Rational, kind of like Array() and String() methods.