From: Jim Freeze Date: 2003-05-08T21:45:51+09:00 Subject: Re: MathN Hi Bret: Thanks for the explanation. This actually looks like a process that could be 'patterned' and used for more than just extending numerics. Have you thought about abstracting this into a Module that had general use? On Sunday, 4 May 2003 at 7:12:42 +0900, Bret Jolly wrote: > Jim Freeze wrote in message news:<20030427200010.A56234@freeze.org>... > > On Monday, 28 April 2003 at 7:16:39 +0900, Bret Jolly wrote: > > > > > > # make infix operators in Numeric classes play nice with Pnom > > > PlayNiceWithPnom = {'+'=>'Plus', '*'=>'Mul', '<=>'=>'Spaceship', > > > '=='=>'Equal', '<'=>'Less', '>'=>'Greater', '<='=>'LessEqual', > > > '>='=>'GreaterEqual'} > > > PlayNiceWithPnom.each {|key, value| > > > eval(%Q% PlayNiceWithPnom#{value} = <<-EOS > > > alias prePnom_#{value} #{key} > > > def #{key}(other) > > > if defined?(Pnom) and other.kind_of?(Pnom) > > > Pnom.new(self) #{key} other > > > else > > > prePnom_#{value}(other) > > > end > > > end > > > EOS > > > %) > > > } > > > ObjectSpace.each_object(Class) {|klass| > > > if klass.ancestors.include?(Numeric) > > > PlayNiceWithPnom.each{|key, value| > > > if klass.public_instance_methods.include?(key) > > > unless klass.public_instance_methods.include?("prePnom_#{value}") > > > eval(%Q% klass.module_eval(PlayNiceWithPnom#{value})%) > > > end > > > end > > > } > > > end > > > } > > > > > > I shudder to think of trying to accomplish this in Python! > > > > The above seems very interesting, but it is not clear to me > > exactly how it works. > > > > Would you mind providing a walk-thru description of what it does? > > > > Thanks > > Sure. I am making a class Pnom of polynomials in > one variable. It's principally an API, but it can also > be used as a calculator. It's designed to play nice > with the standard library provided by mathn, and also > to be usable with Numeric classes that the user > provides. > > The coefficients of a Pnom are (mathematically) > complex numbers. They might have the Ruby class > of Fixnum, Bignum, Rational, Complex, or a subclass > of Numeric provided by the user. A numeric class > provided by the user should be designed to play nice > with the standard classes of mathn, much in the way > in which mathn makes these classes play nice with > one another. > > Though the coefficients must be some sort of > complex number, arguments for evaluation can be > more general. For example, we could evaluate a > Pnom on a Matrix (i.e., substitute the Matrix for > the Pnom's variable and calculate the result). > However, the tricks for this are not part of the > code you asked about. > > In math, in a context dealing with polynomials, > we can treat a constant as a constant polynomial. > Thus I can add a constant to a polynomial to get > a new polynomial: (X**2 - 1) + 3 == X**2 + 2. > To implement this in ruby, we can consider the > possibility that the input is Numeric in the + > method of class Pnom: > > class Pnom > def +(q) > if q.kind_of?(Numeric) > # do something > else > # do something else > end > end > end > > However, this is not enough. We want addition > to be commutative. Thus we have to tell each > subclass of Numeric what to do when adding a Pnom. > For example: > > class Rational > alias PrePnom_Plus + # alias before redefining > > def +(other) > if defined?(Pnom) and other.kind_of?(Pnom) > Pnom.new(self) + other > else > prePnom_Plus(other) > end > end > > Here, Pnom.new is not the normal constructor > (which would just return a number in response > to a single number input number), but a special > constructor which is only used internally, and > which will make a Pnom of degree 0 (or of degree > minus infinity if the input is 0). > > I want to do something like this for a lot > of other methods: *, <=>, <, etc. I want to > do this for all the Numeric classes of the > standard library, and also for other subclasses > of Numeric that the user may have written or > downloaded from the RAA. Such classes have > to be required before pnom.rb is required. > > As an additional complication, I want > to avoid redefining methods which have already > been redefined, as might happen if pnom.rb > is reloaded. > > First I make a hash whose keys are the (string > representations of) methods I want to redefine and > whose values are suffixes to be used in the > alias-before-redefinition. For example, '+' => 'Plus' > since I will alias it to PrePnom_Plus. > > PlayNiceWithPnom = {'+'=>'Plus', '*'=>'Mul', '<=>'=>'Spaceship', > '=='=>'Equal', '<'=>'Less', '>'=>'Greater', '<='=>'LessEqual', > '>='=>'GreaterEqual'} > > Then with for each method in PlayNiceWithPnom, > I create a string representing the alias-before- > redefinition and the new method definition. The > strings will be named PlayNiceWithPnom_Plus, > PlayNiceWithPnom_Mul, PlayNiceWithPnom_Spaceship, > and so forth. > > PlayNiceWithPnom.each {|key, value| > eval(%Q% PlayNiceWithPnom#{value} = <<-EOS > alias prePnom_#{value} #{key} > def #{key}(other) > if defined?(Pnom) and other.kind_of?(Pnom) > Pnom.new(self) #{key} other > else > prePnom_#{value}(other) > end > end > EOS > %) > } > > Now I search the object space for every > class klass which is a subclass of Numeric. For > each such class, and for each of the methods > I'm interested in, I check > (1) if the method is indeed a public_instance_method > of the class. If the class doesn't use this > method I skip it. > (2) if the class has already redefined the method > to play nice with Pnom. For example, if + has > already been redefined, there will be an instance method > PrePnomPlus defined in the class. > > If these conditions hold, I use klass.module_eval > to insert the definition in the class klass. > > I hope this helps! Regards, Bret > > Bret Jolly > http://www.rexx.com/~oinkoink/ > -- Jim Freeze ---------- May a Misguided Platypus lay its Eggs in your Jockey Shorts