Conditional overriding of operators

Discussion in 'Ruby' started by Daniel Vartanov, Nov 4, 2008.

  1. Hello!

    I am trying to replace .+ operator of Numeric class if and only if
    argument (summand) is my own class.

    To be more specific: I am writing geometry module in Ruby and need to
    implement vector-number multiplication so that both:

    2 * vector
    vector * 2

    work well.

    Therefore I need to replace .+ method of Numeric without spoiling
    arithmetical operations with other classes. It is clear, that in case of
    inheritance we can just write:

    class MyNumeric < Numeric
    def +(object)
    super(object) unless object.kind_of?(Vector)

    # vector-number multiplication here...
    end
    end

    But, of course, we need a replacement here. How to implement it?

    class Numeric
    def +(vector)
    unless vector.kinf_of?(Vector)
    # What to write here??!
    end

    # vector-number multiplication here...
    end
    --
    Posted via http://www.ruby-forum.com/.
     
    Daniel Vartanov, Nov 4, 2008
    #1
    1. Advertising

  2. Daniel Vartanov

    Trans Guest

    On Nov 4, 4:52=A0am, Daniel Vartanov <> wrote:
    > Hello!
    >
    > I am trying to replace .+ operator of Numeric class if and only if
    > argument (summand) is my own class.
    >
    > To be more specific: I am writing geometry module in Ruby and need to
    > implement vector-number multiplication so that both:
    >
    > 2 * vector
    > vector * 2
    >
    > work well.
    >
    > Therefore I need to replace .+ method of Numeric without spoiling
    > arithmetical operations with other classes. It is clear, that in case of
    > inheritance we can just write:
    >
    > class MyNumeric < Numeric
    > =A0 def +(object)
    > =A0 =A0 super(object) unless object.kind_of?(Vector)
    >
    > =A0 =A0 # vector-number multiplication here...
    > =A0 end
    > end
    >
    > But, of course, we need a replacement here. How to implement it?


    class Numeric
    alias_method :_plus, :+
    =A0 def +(vector)
    =A0 =A0 unless vector.kind_of?(Vector)
    =A0 =A0 =A0 return _plus(vector)
    =A0 =A0 end

    =A0 =A0 # vector-number multiplication here...
    end

    But maybe better something like:

    class Numeric
    def self.op_map(op, klass=3Dnil, &block)
    op =3D op.to_sym
    @op_map ||=3D {}
    @op_map[op] ||=3D {}
    return @op_map[op] unless klass
    return @op_map[op][klass] unless block
    @op_map[op][klass] =3D block
    end

    alias_method :_plus, :+

    def +(operand)
    if procedure =3D self.class.op_map[:+][operand.class]
    procedure.call(operand)
    else
    _plus(vector)
    end
    end
    end

    Numeric.op_map:)+,Vector) do |operand|
    # ... your vector addition here
    end

    T.
     
    Trans, Nov 4, 2008
    #2
    1. Advertising

  3. Daniel Vartanov

    Pit Capitain Guest

    2008/11/4 Daniel Vartanov <>:
    > I am trying to replace .+ operator of Numeric class if and only if
    > argument (summand) is my own class.
    >
    > To be more specific: I am writing geometry module in Ruby and need to
    > implement vector-number multiplication so that both:
    >
    > 2 * vector
    > vector * 2
    >
    > work well.


    Daniel, look at [ruby-talk:98763], for example here:

    http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/98763

    Regards,
    Pit
     
    Pit Capitain, Nov 4, 2008
    #3
  4. 2008/11/4 Daniel Vartanov <>:
    > I am trying to replace .+ operator of Numeric class if and only if
    > argument (summand) is my own class.
    >
    > To be more specific: I am writing geometry module in Ruby and need to
    > implement vector-number multiplication so that both:
    >
    > 2 * vector
    > vector * 2
    >
    > work well.
    >
    > Therefore I need to replace .+ method of Numeric without spoiling
    > arithmetical operations with other classes.


    No, you don't. You need to implement #coerce and #+, #- etc. in
    *your* class properly (see the link Pit provided for one, albeit not
    very complete example). Basically your coerce is invoked when Fixnum#+
    sees an instance of your class so you are in control what operation is
    finally implemented.

    You can find more hits with
    http://blade.nagaokaut.ac.jp/cgi-bi...ubmit=Search&dbname=ruby-talk&max=50&whence=0

    Cheers

    robert

    --
    remember.guy do |as, often| as.you_can - without end
     
    Robert Klemme, Nov 4, 2008
    #4
  5. Daniel Vartanov

    Andrea Fazzi Guest

    Daniel Vartanov ha scritto:
    > Hello!
    >
    > I am trying to replace .+ operator of Numeric class if and only if
    > argument (summand) is my own class.
    >
    > To be more specific: I am writing geometry module in Ruby and need to
    > implement vector-number multiplication so that both:
    >
    > 2 * vector
    > vector * 2
    >
    > work well.
    >
    > Therefore I need to replace .+ method of Numeric without spoiling
    > arithmetical operations with other classes.
    >


    Hi Daniel,

    take a look at:

    http://www.splatbang.com/rubyquiz/quiz.rhtml?id=179_Modular_Arithmetic

    Hope that helps.
    Andrea
     
    Andrea Fazzi, Nov 4, 2008
    #5
  6. Thanks, guys! coercing mechanism is really useful. Due to multiplication
    is commutative, I just swapped multiplier in coerce method, so even if
    (2 * vector) is called, it calls vector.+(2) finally.

    def *(scalar)
    Vector.new(x * scalar, y * scalar)
    end

    def coerce(scalar)
    [self, scalar]
    end

    vector * 2 # vector.*(2) is called
    2 * vector # 2.*(vector), then vector.coerce(2), then vector.*(2)

    Thanks again! :)
    --
    Posted via http://www.ruby-forum.com/.
     
    Daniel Vartanov, Nov 4, 2008
    #6
  7. 2008/11/4 Daniel Vartanov <>:
    > Thanks, guys! coercing mechanism is really useful. Due to multiplication
    > is commutative, I just swapped multiplier in coerce method, so even if
    > (2 * vector) is called, it calls vector.+(2) finally.
    >
    > def *(scalar)
    > Vector.new(x * scalar, y * scalar)
    > end
    >
    > def coerce(scalar)
    > [self, scalar]
    > end


    I am not sure whether this implementation adheres to the contract of
    #coerce. Normally you need to return the representative of the
    *other* instance first:

    irb(main):002:0> 1.coerce 2.0
    => [2.0, 1.0]

    It may be ok in your case though. Note also, that you do no type
    checking in #* which you should do in order to properly react on
    values other than scalars. IMHO normally the logic should be "if the
    other instance is not of the same class as self invoke #coerce on it".

    > vector * 2 # vector.*(2) is called
    > 2 * vector # 2.*(vector), then vector.coerce(2), then vector.*(2)


    Kind regards

    robert

    --
    remember.guy do |as, often| as.you_can - without end
     
    Robert Klemme, Nov 4, 2008
    #7
  8. Hi, Robert.

    > I am not sure whether this implementation adheres to the contract of
    > #coerce. Normally you need to return the representative of the
    > *other* instance first:
    > irb(main):002:0> 1.coerce 2.0
    > => [2.0, 1.0]


    Swapping is the main point here, we exploit commutativity of
    multiplication. If Numeric can be *converted back* to your type, you can
    use this approach (actually you mentioned it in one of threads ;)):
    def coerce(x) [self.class.new( x ), self]; end

    But Numeric cannot be converted back to Vector. Just look what people do
    in case of subtraction, which is not commutative:
    http://groups.google.com/group/ruby-talk-google/browse_thread/thread/aa288902532dcc6c
    (note, he uses swapping too)

    > Note also, that you do no type checking in #* which you should do in order to
    > properly react on values other than scalars.

    I think you a right here, thanks for comment. I relied on the Vector.*
    method, where an exception will be raised if numerical components of
    vector is multiplied by something else than Numeric. But such exception
    is not descriptive enough, so I decided to include such checking:

    def coerce(scalar)
    if scalar.is_a?(Numeric)
    [self, scalar]
    else
    raise ArgumentError, "Vector: cannot coerce #{scalar.inspect}"
    end
    end

    --
    Posted via http://www.ruby-forum.com/.
     
    Daniel Vartanov, Nov 4, 2008
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Nicodemus
    Replies:
    0
    Views:
    289
    Nicodemus
    Jul 17, 2003
  2. Andrew Durdin

    Overriding logical operators?

    Andrew Durdin, Aug 21, 2004, in forum: Python
    Replies:
    3
    Views:
    428
    Michael Hudson
    Aug 23, 2004
  3. Terry Reedy

    Re: Overriding logical operators?

    Terry Reedy, Aug 21, 2004, in forum: Python
    Replies:
    2
    Views:
    342
    Terry Reedy
    Aug 24, 2004
  4. Andrew Durdin

    Fwd: Overriding logical operators?

    Andrew Durdin, Aug 21, 2004, in forum: Python
    Replies:
    1
    Views:
    351
    Greg Ewing
    Aug 24, 2004
  5. Andrew Durdin

    Re: Overriding logical operators?

    Andrew Durdin, Aug 21, 2004, in forum: Python
    Replies:
    1
    Views:
    384
    Oliver Fromme
    Aug 23, 2004
Loading...

Share This Page