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

on 2008-11-04 10:54

on 2008-11-04 11:20

On Nov 4, 4:52 am, Daniel Vartanov <daniel.varta...@gmail.com> wrote: > > # vector-number multiplication here... > end > end > > But, of course, we need a replacement here. How to implement it? class Numeric alias_method :_plus, :+ def +(vector) unless vector.kind_of?(Vector) return _plus(vector) end # vector-number multiplication here... end But maybe better something like: class Numeric def self.op_map(op, klass=nil, &block) op = op.to_sym @op_map ||= {} @op_map[op] ||= {} return @op_map[op] unless klass return @op_map[op][klass] unless block @op_map[op][klass] = block end alias_method :_plus, :+ def +(operand) if procedure = 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.

on 2008-11-04 13:15

2008/11/4 Daniel Vartanov <daniel.vartanov@gmail.com>: > 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/... Regards, Pit

on 2008-11-04 13:46

2008/11/4 Daniel Vartanov <daniel.vartanov@gmail.com>: > > 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-bin/vframe.rb?key... Cheers robert

on 2008-11-04 14:52

Daniel Vartanov ha scritto: > > 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=17... Hope that helps. Andrea

on 2008-11-04 15:14

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! :)

on 2008-11-04 16:48

2008/11/4 Daniel Vartanov <daniel.vartanov@gmail.com>: > 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

on 2008-11-04 17:29

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/br... (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