Ruby 使用多个参数重载运算符

Ruby overloading operators with severals parameters

我有一个 class MoneyBox,它有两个字段(钱包和美分)。我需要用这些字段重载操作 +、- 和 *。如何正确实现这些带两个参数的运算符的重载?

class MoneyBox
  attr_accessor :wallet, :cent

  def initialize(wallet, cent)
  @wallet = wallet  
  @cent = cent 
   
  end

  def + (obj, obj_cent)
    self.wallet = self.wallet + obj.wallet
    self.cent = self.cent + obj_cent.cent
    return self.wallet, self.cent 
  end

  def - (obj, obj_cent) 
    self.wallet = self.wallet - obj.wallet
    self.cent = self.cent - obj_cent.cent
    return self.wallet, self.cent 
  end

  def * (obj,obj_cent) 
    self.wallet = self.wallet * obj.wallet
    self.cent = self.cent * obj_cent
    return self.wallet, self.cent 
  end

end

应该是这样的:

Cash1 = MoneyBox.new(500, 30)
Cash2 = MoneyBox.new(100, 15)

puts " D1 = #{D1 = (Cash1-(Cash2*2))}" # 500,30 - 100,15*2 = 300,0

如果要重载运算符以接受两个参数,则不能使用典型的 x + y 语法。

相反,您必须使用 .+,请参阅以下内容:

class Foo
  def initialize(val)
    @val = val
  end
   
  def +(a,b)
    @val + a + b
  end
end

foo = Foo.new(1)

foo.+(2,3)
# => 6

您的示例用法与方法定义不匹配。您想以编写表达式的方式使用您的对象,例如 Cash2 * 2)。由于 Cash2 是 class MoneyBox 的常量,因此标量乘法的定义类似于

def * (factor)
  self.class.new(wallet, cent * factor)
end

这是

的快捷方式
def * (factor)
  MoneyBox.new(self.wallet, self.cent * factor)
end

您已经有一个 class 封装了钱包/美分对,并且操作也是成对执行的。为什么不利用它并使 +-* 将(单个)MoneyBox 实例作为它们的参数,并将 return 结果作为另一个 MoneyBox 实例,例如:(算术运算符不应修改其操作数)

class MoneyBox
  attr_accessor :wallet, :cent

  def initialize(wallet, cent)
    @wallet = wallet
    @cent = cent
  end

  def +(other)
    MoneyBox.new(wallet + other.wallet, cent + other.cent)
  end

  def -(other)
    MoneyBox.new(wallet - other.wallet, cent - other.cent)
  end

  def *(other)
    MoneyBox.new(wallet * other.wallet, cent * other.cent)
  end

  def to_s
    "#{wallet},#{cent}"
  end
end

用法示例:

cash1 = MoneyBox.new(500, 30)
cash2 = MoneyBox.new(100, 15)

puts "#{cash1} + #{cash2} = #{cash1 + cash2}"
# 500,30 + 100,15 = 600,45

puts "#{cash1} - #{cash2} = #{cash1 - cash2}"
# 500,30 - 100,15 = 400,15

puts "#{cash1} * #{cash2} = #{cash1 * cash2}"
# 500,30 * 100,15 = 50000,45

要将 walletcents 都乘以 2,您需要使用 MoneyBox.new(2, 2) 实例:

puts "#{cash1} - #{cash2} * 2 = #{cash1 - cash2 * MoneyBox.new(2, 2)}"
# 500,30 - 100,15 * 2 = 300,0

请注意运算符 precedence 仍然适用,因此结果计算为 cash1 - (cash2 * Money.new(2, 2))

如果您想直接乘以整数,即不显式创建 MoneyBox.new(2, 2) 实例,您可以通过添加条件将逻辑移至 *,例如:

  def *(other)
    case other
    when Integer
      MoneyBox.new(wallet * other, cent * other)
    when MoneyBox
      MoneyBox.new(wallet * other.wallet, cent * other.cent)
    else
      raise ArgumentError, "expected Integer or MoneyBox, got #{other.class}"
    end
  end

这给你:

cash1 = MoneyBox.new(500, 30)
cash2 = MoneyBox.new(100, 15)

puts "#{cash1} - #{cash2} * 2 = #{cash1 - cash2 * 2}"
# 500,30 - 100,15 * 2 = 300,0

请注意,这仅定义 MoneyBox#* 而不会更改 Integer#*,因此 2 * cash2 默认情况下 有效:

2 * cash2
# TypeError: MoneyBox can't be coerced into Integer

那是因为您在 2 上调用 *,而 Integer 不知道如何处理 MoneyBox 实例。这可以通过在 MoneyBox 中实施 coerce 来解决:(仅适用于数字的东西)

  def coerce(other)
    [self, other]
  end

这有效地将 2 * cash2 变成了 cash2 * 2


顺便说一句,如果你总是用一个整数调用 * 并且不需要实际乘以两个 MoneyBox 实例,它当然会变得更简单:

  def *(factor)
    MoneyBox.new(wallet * factor, cent * factor)
  end