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 * 5
和 5 * 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++运算符重载中 重载为类的成员函数和重载为类的友元函数 的区别是啥?