Menu
Coddy logo textTech

O Módulo Comparable

Parte da seção Programação orientada a objetos do Journey de Ruby da Coddy. Lição 29 de 57.

O módulo integrado do Ruby Comparable fornece a uma classe os operadores <, <=, ==, >, >= e os métodos between? e clamp. Ele precisa de um método da classe: <=>, que retorna um número negativo, 0 ou um número positivo quando o objeto é menor, igual ou maior:

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))

Saída:

true
true
true

Números e strings já têm <=>, então uma classe geralmente delega a um de seus valores. A comparação de arrays funciona elemento por elemento, o que permite ordenar por vários campos de uma só vez:

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

Saída:

true
true

sort, min e max também usam <=>, portanto objetos da classe podem ser ordenados sem um bloco. clamp mantém um valor entre dois outros:

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))

Saída:

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

<=> deve retornar nil quando o outro objeto não puder ser comparado. Em seguida, Comparable gera ArgumentError para < e retorna false para ==, em vez de falhar com um confuso NoMethodError:

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

  grams <=> other.grams
end

Weight.new(1) == 5     # false
Weight.new(1) < 5      # ArgumentError: comparison of Weight with 5 failed
challenge icon

Desafio

Fácil

Complete a classe Weight. Weight.parse(text) lê 500g, 1.2kg ou 2lb (1 lb equivale a 453.6 g) e armazena os gramas. Inclua Comparable e defina <=> com base nos gramas. to_s retorna os gramas arredondados para um número inteiro, como 1200g.

O código fornecido analisa cada linha de entrada e, em seguida, imprime os pesos ordenados, o mais pesado, o mais leve, se o primeiro peso está entre 1 kg e 2 kg e o primeiro peso limitado a no máximo 1 kg.

Seu código deve ficar em weight.rb. main.rb contém o código fornecido de entrada/saída e não pode ser editado.

Experimente você mesmo

require_relative 'weight'

# Código de entrada/saída fornecido: mantenha-o como está
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 iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação orientada a objetos

Pratique por conta própria: Compilador de Ruby online