ruby 运算符重载问题

Posted

技术标签:

【中文标题】ruby 运算符重载问题【英文标题】:ruby operator overloading question 【发布时间】:2010-12-23 16:14:03 【问题描述】:

为了娱乐目的,我一直在玩弄 ruby​​ 和 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 的乘法而不完全替换它。这可能在红宝石中吗?有什么建议吗?

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

【问题讨论】:

为了记录,把@x*s, @y*s, @z*s改成@x*a, @y*a, @z*a,否则你的代码就坏了。 谢谢,一次从 2 个地方复制代码 > 【参考方案1】:

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

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 表示强制。代表必须调试您的代码的人,除非绝对必要,否则请不要对核心类进行猴子补丁。 这非常适合我的需要。如果我遇到一个不能交换的类似示例,那么我想我会根据需要进行猴子补丁;) 强制应该总是返回 self 作为第二个参数!否则你会弄乱论点的交换性。相反,coerce 应该将参数转换为可以相乘的类型(例如:Vec3.new(other, other, other))。显然这不是没有它自己的问题。【参考方案2】:

我相信下面会做你想要的,虽然banister's suggestion 使用coerce 而不是猴子补丁Numeric 是首选方法。仅在必要时使用此方法(例如,如果您只希望 some 二进制操作数是可传递的)。

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

【讨论】:

mmm sexy :) 顺便说一句,我需要将第一行更改为“Fixnum.class_eval do”或(等效?)“class Fixnum” 难道不能直接在没有class_eval的Fixnum类上定义它,并且做一个常规的def而不是define_method吗? alias_method 不必要地污染了命名空间。这个成语既不是“荒谬的”(它是一个众所周知的成语,其内部工作原理以及必要性已在许多博客文章中详细记录)也不是“完全没有必要”(事实上是,唯一的成语,AFAIK)。 不过,banister 对coerce 的建议对于交换性问题来说是一个更好的解决方案。为它 +1。 哦,还有栏杆:“普通类主体”对于 Numeric 来说可以正常工作,此时肯定已定义,但在其他情况下很危险,因为它可能会阻止 autoload 声明射击。

以上是关于ruby 运算符重载问题的主要内容,如果未能解决你的问题,请参考以下文章

重载运算符问题

运算符重载

想问大佬++操作符重载,前置和后置的问题?

C++运算符重载中 重载为类的成员函数和重载为类的友元函数 的区别是啥?

GroovyGroovy 运算符重载 ( 运算符重载 | 运算符重载对应方法 )

关于双目运算符的重载问题