Menu
CoddyTech

Module Comparable

Thuộc phần Lập trình hướng đối tượng trong hành trình Ruby của Coddy. Bài học 29 trên 57.

Mô-đun tích hợp sẵn Comparable của Ruby cung cấp cho một lớp các toán tử <, <=, ==, >, >= và các phương thức between? và clamp. Mô-đun cần lớp định nghĩa một phương thức: <=>, phương thức này trả về một số âm, 0 hoặc một số dương khi đối tượng nhỏ hơn, bằng hoặc lớn hơn:

class Weight
  include Comparable
  attr_reader :grams

  def initialize(grams)
    @grams = grams
  end

  def <=>(other)
    grams <=> other.grams
  end
end

a = Weight.new(500)
b = Weight.new(1200)
puts a < b
puts a == Weight.new(500)
puts b.between?(a, Weight.new(2000))

Đầu ra:

true
true
true

Số và chuỗi đã có <=>, nên một lớp thường ủy quyền việc so sánh cho một trong các giá trị của nó. Khi so sánh mảng, chương trình xét từng phần tử, nhờ đó có thể sắp xếp theo nhiều trường cùng lúc:

class Person
  include Comparable
  attr_reader :last, :first

  def initialize(first, last)
    @first = first
    @last = last
  end

  def <=>(other)
    [last, first] <=> [other.last, other.first]
  end
end

a = Person.new("Ada", "Lovelace")
b = Person.new("Alan", "Turing")
c = Person.new("Byron", "Lovelace")
puts a < c
puts b > c

Đầu ra:

true
true

sort, min và max cũng dùng <=>, nên bạn có thể sắp xếp các đối tượng của lớp mà không cần khối lệnh. clamp giữ một giá trị trong khoảng giữa hai giá trị khác:

class Weight
  include Comparable
  attr_reader :grams

  def initialize(grams)
    @grams = grams
  end

  def <=>(other)
    grams <=> other.grams
  end

  def to_s
    "#{grams}g"
  end
end

list = [Weight.new(900), Weight.new(150), Weight.new(400)]
puts list.sort.join(", ")
puts list.max
puts Weight.new(5000).clamp(Weight.new(0), Weight.new(1000))

Đầu ra:

150g, 400g, 900g
900g
1000g

<=> nên trả về nil khi không thể so sánh với đối tượng còn lại. Khi đó, Comparable sẽ phát sinh ArgumentError với < và trả về false với ==, thay vì gặp lỗi NoMethodError khó hiểu:

def <=>(other)
  return nil unless other.is_a?(Weight)

  grams <=> other.grams
end

Weight.new(1) == 5     # false
Weight.new(1) < 5      # ArgumentError: không thể so sánh Weight với 5
challenge icon

Thử thách

Dễ

Hoàn thiện lớp Weight. Weight.parse(text) đọc 500g, 1.2kg hoặc 2lb (1 lb bằng 453.6 g) và lưu số gam. Thêm Comparable và định nghĩa <=> dựa trên số gam. to_s trả về số gam được làm tròn thành số nguyên, như 1200g.

Code được cung cấp phân tích từng dòng đầu vào, sau đó in các khối lượng theo thứ tự đã sắp xếp, khối lượng nặng nhất, nhẹ nhất, liệu khối lượng đầu tiên có nằm trong khoảng từ 1 kg đến 2 kg hay không, và khối lượng đầu tiên sau khi giới hạn ở mức tối đa 1 kg.

Đặt code của bạn trong weight.rb. main.rb chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa tệp này.

Tự làm thử

require_relative 'weight'

# Đoạn code đầu vào/đầu ra đã cho: giữ nguyên
input = []
while (line = gets)
  input << line.chomp
end
weights = input.map { |line| Weight.parse(line) }
puts "sorted: #{weights.sort.join(", ")}"
puts "heaviest: #{weights.max}"
puts "lightest: #{weights.min}"
puts "first between 1kg and 2kg: #{weights[0].between?(Weight.parse("1kg"), Weight.parse("2kg"))}"
puts "first clamped: #{weights[0].clamp(Weight.parse("0g"), Weight.parse("1kg"))}"
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Ruby online