运算符方法
本课属于 Coddy Ruby 学习旅程的“面向对象编程”单元。第 38 课,共 57 课。
大多数 Ruby 运算符都是方法。a + b 会调用 a.+(b),因此类可以像定义其他方法一样定义 +。对值对象进行算术运算时,会返回一个新对象,并且不会改变两个操作数:
class Vector
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
end
def +(other)
Vector.new(x + other.x, y + other.y)
end
def to_s
"(#{x}, #{y})"
end
end
a = Vector.new(1, 2)
b = Vector.new(3, 4)
puts a + b
puts a输出:
(4, 6)
(1, 2)右操作数可以是另一种类型的值,例如用于缩放的数字。单目负号对应的方法是 -@:
class Vector
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
end
def -(other)
Vector.new(x - other.x, y - other.y)
end
def *(factor)
Vector.new(x * factor, y * factor)
end
def -@
Vector.new(-x, -y)
end
def to_s
"(#{x}, #{y})"
end
end
v = Vector.new(3, 4)
puts v * 2
puts v - Vector.new(1, 1)
puts(-v)输出:
(6, 8)
(2, 3)
(-3, -4)定义了 + 后,+= 也能使用,因为 a += b 的意思是 a = a + b。为 sum 提供一个该类的初始值后,它也能正常工作:
class Money
attr_reader :cents
def initialize(cents)
@cents = cents
end
def +(other)
Money.new(cents + other.cents)
end
def to_s
format("$%.2f", cents / 100.0)
end
end
total = Money.new(0)
total += Money.new(250)
total += Money.new(199)
puts total
puts [Money.new(100), Money.new(5)].sum(Money.new(0))输出:
$4.49
$1.05<=> 也是一个运算符。结合 Comparable,它可以让 Money 对象进行比较和排序;运算符也可以像数字运算那样,通过引发异常来拒绝不匹配的操作数:
class Money
include Comparable
attr_reader :cents, :currency
def initialize(cents, currency)
@cents = cents
@currency = currency
end
def +(other)
raise ArgumentError, "cannot add #{other.currency} to #{currency}" unless other.currency == currency
Money.new(cents + other.cents, currency)
end
def <=>(other)
cents <=> other.cents
end
end
puts Money.new(500, "EUR") > Money.new(300, "EUR")
begin
Money.new(1, "EUR") + Money.new(1, "USD")
rescue ArgumentError => e
puts e.message
end输出:
true
cannot add USD to EUR挑战
简单补全 Vector 类。为两个向量定义 + 和 -,为向量和数字定义 *,定义一元负号 -@、比较两个坐标的 ==,以及返回 (4, 6) 的 to_s。length 使用 Math.sqrt 返回向量的长度。
提供的代码会读取表达式:1,2 + 3,4、1,2 - 3,4、1,2 * 3、neg 1,2 和 1,2 == 1,2。它会输出每个结果;对于向量结果,还会输出 length 7.21,保留两位小数。
你的代码写在 vector.rb 中。main.rb 保存了提供的输入/输出代码,不能编辑。
亲手试试
require_relative 'vector'
# 提供的输入输出代码:保持原样
input = []
while (line = gets)
input << line.chomp
end
def vec(text)
Vector.new(*text.split(",").map(&:to_i))
end
input.each do |line|
parts = line.split
result = if parts[0] == "neg" then -vec(parts[1])
else
a = vec(parts[0])
case parts[1]
when "+" then a + vec(parts[2])
when "-" then a - vec(parts[2])
when "*" then a * parts[2].to_i
else a == vec(parts[2])
end
end
puts result
puts format("length %.2f", result.length) if result.is_a?(Vector)
end
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Ruby 在线编译器