ruby运算符重载问题

时间:2009-12-06 02:59:53

标签: ruby operators operator-overloading overloading

为了娱乐目的,我一直在使用红宝石和opengl,我决定写一些3d矢量/平面/等等类来完成一些数学运算。

简化示例:

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,1)
v2 = v1*5 #produces [5,5,5]

哪一切都很精致,但我也希望能够写出

v2 = 5*v1

这需要向Fixnum或Float或其他任何东西添加功能,但我找不到重载或扩展fixnum的乘法而不完全替换它的方法。红宝石有可能吗?任何提示?

(显然我可以按照正确的顺序编写所有的乘法,如果需要的话)

2 个答案:

答案 0 :(得分:22)

使用强制是一种比猴子修补核心类更好的方法:

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

如果您将v定义为v = Vec3.new,那么以下内容将有效:v * 55 * v coerce(self)返回的第一个元素成为操作的新接收者,第二个元素(other)成为参数,因此5 * v完全等同于v * 5

答案 1 :(得分:-1)

我相信以下内容可以满足您的需求,但banister's suggestion使用coerce代替猴子修补Numeric是首选方法。仅在必要时使用此方法(例如,如果您只希望某些二进制操作数是可传递的)。

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