开发者

ruby operator overloading question

开发者 https://www.devze.com 2022-12-13 13:09 出处:网络
i\'ve been messing around with ruby and opengl for entertainment purposes, and i decided to write some 3d vector/plane/etc classes to pretty up some of the math.

i've been messing around with ruby and opengl for entertainment purposes, and i decided to write some 3d vector/plane/etc classes to pretty up some of the math.

simplified example:

class Vec3
    attr_accessor :x,:y,:z

    def *(a)
        if a.is_a?(Numeric) #multiply by scalar
            return Vec3.new(@x*a, @y*a, @z*a)
        elsif a.is_a?(Vec3) #dot product
            return @x*a.x + @y*a.y + @z*a.z
        end
    end
end

v1 = Vec3.new(1,1开发者_如何学Python,1)
v2 = v1*5 #produces [5,5,5]

which all fine and dandy, but i also want to be able to write

v2 = 5*v1

which requires adding functionality to Fixnum or Float or whatever, but i couldn't find a way to overload or extend fixnum's multiplication without replacing it entirely. is this possible in ruby? any tips?

(obviously i can just write all my multiplications in the correct order if i need to)


Using coerce is a MUCH better approach than monkey-patching a core class:

class Vec3
    attr_accessor :x,:y,:z

    def *(a)
        if a.is_a?(Numeric) #multiply by scalar
            return Vec3.new(@x*a, @y*a, @z*a)
        elsif a.is_a?(Vec3) #dot product
            return @x*a.x + @y*a.y + @z*a.z
        end
    end

    def coerce(other)
        return self, other
    end
end

if you define v as v = Vec3.new then the following will work: v * 5 and 5 * v The first element returned by coerce (self) becomes the new receiver for the operation, and the second element (other) becomes the parameter, so 5 * v is exactly equivalent to v * 5


I believe the following will do what you want, though banister's suggestion to use coerce instead of monkey-patching Numeric is a preferred method. Use this method only if necessary (for example if you only want some binary operands to be transitive).

Fixnum.class_eval do
  original_times = instance_method(:*)
  define_method(:*) do |other|
    if other.kind_of?(Vec3)
      return other * self
    else
      return original_times.bind(self).call(other)
    end
  end
end
0

精彩评论

暂无评论...
验证码 换一张
取 消