Ruby:仅在某些情况下重载运算符行为
Ruby: overload operator behaviour for some cases only
我的问题是:如何在内置 class(例如 Integer.new.+)上重载运算符,但仅限于某些情况,具体取决于 class第二个操作数
这是我正在寻找的行为:
myObject = myClass.new
1 + myObject #=> special behaviour
1 + 2 #=> default behaviour (3)
例如,在 Python 中,我会在 myClass 上定义一个 __radd__
方法来覆盖案例 1。
我试过使用 super
但显然 Numeric
没有运算符方法。
理想情况下,我正在寻找一种提取 +
方法并重命名它的方法。
像这样:
class Integer
self.plus = self.+ # you know what i mean, I don't know how else to express this.
# I also know that methods don't work like this, this is just to
# illustrate a point.
def + other
other.class == myClass ? special behaviour : self.plus other
end
end
感谢您的帮助
到目前为止,此处发布的两种方法都是遗留 Rails 方法,完全错误。它依赖于 class 没有名为 plus
的方法 并且 没有人会重新打开 class 来创建名为 [=11= 的方法].否则事情会变得疯狂。
正确的解法是Module#prepend
:
Integer.prepend(Module.new do
def + other
case other
when Fixnum then special_behaviour
else super(other)
end
end
end)
是的,您可以重写标准库中几乎所有内容的行为来实现某个结果,但这会损害对代码的理解,并在将来的某个时候回来咬您一口。
在这种特殊情况下,Fixnum#+
设计为采用数值,return 设计为采用数字结果。如果我们想定义自己的 classes 来与 Fixnum#+
交互,我们需要理解设计契约并遵守它。
Ruby 中的一般约定是使用鸭子类型。我们不关心对象的class,我们只关心它的行为是否像/可以转换成我们想要的对象。例如:
class StringifiedNumber
def initialize(number)
@number = number
end
# String#+ calls to_str on any object passed to it
def to_str
# replace with number to string parsing logic
"one hundred"
end
end
> "total: " + StringifiedNumber.new(100)
=> "total: one hundred"
数字有点复杂,因为您可以混合使用整数、浮点数、复数等。处理此问题的惯例是定义一个 coerce
方法,其中 return 有两个元素相同类型的,然后用于执行请求的操作。
class NumberfiedString
def initialize(string)
@string = string
end
def to_i
# replace with complicated natural language parsing logic
100
end
def +(other_numberfied_string)
NumberfiedString.new(self.to_i + other_numberfied_string.to_i)
end
# For types which are not directly supported,
# Fixnum#+(target) will call the equivalent of
# target.coerce[0] + target.coerce[1]
def coerce(other)
[NumberfiedString.new(other.to_s), self]
end
end
> NumberfiedString.new("one hundred") + NumberfiedString.new("one hundred")
=> #<NumberfiedString:0x007fadbc036d28 @string=200>
> 100 + NumberfiedString.new("one hundred")
=> #<NumberfiedString:0x007fadbc824c88 @string="200">
回答 OP 的后续问题:
Is there no equivalent to Python's radd and related methods? (Where,
if the first operand doesn't support the operation or the types, the
second operand takes over)
class MyClass
def +(other)
puts "called +"
end
def coerce(other)
[self, other]
end
end
> 1 + MyClass.new
called +
=> nil
我的问题是:如何在内置 class(例如 Integer.new.+)上重载运算符,但仅限于某些情况,具体取决于 class第二个操作数
这是我正在寻找的行为:
myObject = myClass.new
1 + myObject #=> special behaviour
1 + 2 #=> default behaviour (3)
例如,在 Python 中,我会在 myClass 上定义一个 __radd__
方法来覆盖案例 1。
我试过使用 super
但显然 Numeric
没有运算符方法。
理想情况下,我正在寻找一种提取 +
方法并重命名它的方法。
像这样:
class Integer
self.plus = self.+ # you know what i mean, I don't know how else to express this.
# I also know that methods don't work like this, this is just to
# illustrate a point.
def + other
other.class == myClass ? special behaviour : self.plus other
end
end
感谢您的帮助
到目前为止,此处发布的两种方法都是遗留 Rails 方法,完全错误。它依赖于 class 没有名为 plus
的方法 并且 没有人会重新打开 class 来创建名为 [=11= 的方法].否则事情会变得疯狂。
正确的解法是Module#prepend
:
Integer.prepend(Module.new do
def + other
case other
when Fixnum then special_behaviour
else super(other)
end
end
end)
是的,您可以重写标准库中几乎所有内容的行为来实现某个结果,但这会损害对代码的理解,并在将来的某个时候回来咬您一口。
在这种特殊情况下,Fixnum#+
设计为采用数值,return 设计为采用数字结果。如果我们想定义自己的 classes 来与 Fixnum#+
交互,我们需要理解设计契约并遵守它。
Ruby 中的一般约定是使用鸭子类型。我们不关心对象的class,我们只关心它的行为是否像/可以转换成我们想要的对象。例如:
class StringifiedNumber
def initialize(number)
@number = number
end
# String#+ calls to_str on any object passed to it
def to_str
# replace with number to string parsing logic
"one hundred"
end
end
> "total: " + StringifiedNumber.new(100)
=> "total: one hundred"
数字有点复杂,因为您可以混合使用整数、浮点数、复数等。处理此问题的惯例是定义一个 coerce
方法,其中 return 有两个元素相同类型的,然后用于执行请求的操作。
class NumberfiedString
def initialize(string)
@string = string
end
def to_i
# replace with complicated natural language parsing logic
100
end
def +(other_numberfied_string)
NumberfiedString.new(self.to_i + other_numberfied_string.to_i)
end
# For types which are not directly supported,
# Fixnum#+(target) will call the equivalent of
# target.coerce[0] + target.coerce[1]
def coerce(other)
[NumberfiedString.new(other.to_s), self]
end
end
> NumberfiedString.new("one hundred") + NumberfiedString.new("one hundred")
=> #<NumberfiedString:0x007fadbc036d28 @string=200>
> 100 + NumberfiedString.new("one hundred")
=> #<NumberfiedString:0x007fadbc824c88 @string="200">
回答 OP 的后续问题:
Is there no equivalent to Python's radd and related methods? (Where, if the first operand doesn't support the operation or the types, the second operand takes over)
class MyClass
def +(other)
puts "called +"
end
def coerce(other)
[self, other]
end
end
> 1 + MyClass.new
called +
=> nil