Menu
Coddy logo textTech

Collections personnalisées

Fait partie de la section Programmation orientée objet du Journey Ruby de Coddy. Leçon 40 sur 57.

Une classe de collection combine plusieurs méthodes spéciales : each avec Enumerable, << pour l’ajout, size, ainsi que to_s et inspect pour l’affichage. each doit également fonctionner sans bloc, en renvoyant un énumérateur avec to_enum, comme le font les tableaux :

class TodoList
  include Enumerable

  def initialize
    @items = []
  end

  def <<(item)
    @items << item
    self
  end

  def each
    return to_enum(:each) unless block_given?

    @items.each { |item| yield item }
    self
  end
end

list = TodoList.new << "wash" << "shop" << "cook"
list.each.with_index(1) { |item, n| puts "#{n}. #{item}" }
p list.select { |i| i.include?("o") }

Résultat :

1. wash
2. shop
3. cook
["shop", "cook"]

block_given? indique si l'appelant a fourni un bloc. L'énumérateur renvoyé en l'absence de bloc permet des enchaînements tels que each.with_index, each_slice ou next :

class Countdown
  include Enumerable

  def initialize(from)
    @from = from
  end

  def each
    return to_enum(:each) unless block_given?

    @from.downto(1) { |n| yield n }
  end
end

e = Countdown.new(3).each
puts e.next
puts e.next
p Countdown.new(5).each_slice(2).to_a

Sortie :

3
2
[[5, 4], [3, 2], [1]]

to_s est le texte convivial destiné aux utilisateurs, tandis que inspect fournit un bref résumé pour les programmeurs. Pour une collection, inspect affiche généralement la classe et la taille plutôt que chaque élément :

class TodoList
  include Enumerable

  def initialize(*items)
    @items = items
  end

  def each(&block)
    @items.each(&block)
  end

  def size
    @items.size
  end

  def to_s
    map.with_index(1) { |item, n| "#{n}. #{item}" }.join("\n")
  end

  def inspect
    "#<TodoList #{size} items>"
  end
end

list = TodoList.new("wash", "shop")
puts list
p list

Sortie :

1. wash
2. shop
#<TodoList 2 items>

Enumerable fournit count, mais pas size, empty?, << ou [] : ceux-ci appartiennent à la classe, car elle seule sait comment les éléments sont stockés. Les définir donne à la collection l’apparence d’un tableau pour ses appelants :

def size
  @items.size
end

def empty?
  @items.empty?
end

def [](index)
  @items[index]
end
challenge icon

Défi

Facile

Complétez la classe TodoList. << ajoute le texte d’une tâche et renvoie la liste. each fournit les tâches et renvoie to_enum(:each) en l’absence de bloc. Incluez Enumerable. size renvoie le nombre de tâches. to_s renvoie les tâches sous forme de lignes numérotées, 1. wash, ou nothing to do, et inspect renvoie #<TodoList 3 items>.

Le code fourni ajoute chaque ligne d’entrée en chaînant <<, puis affiche la liste, le résultat de p appliqué à la liste, les tâches numérotées avec ! via each.with_index(1) sous la forme 1! wash, le nombre de tâches contenant o, ainsi que la première tâche par ordre alphabétique.

Votre code doit être placé dans todo_list.rb. main.rb contient le code d’entrée/sortie fourni et ne peut pas être modifié.

Essayez vous-même

require_relative 'todo_list'

# Code d'entrée/sortie fourni : le garder tel quel
input = []
while (line = gets)
  input << line.chomp
end
list = input.reduce(TodoList.new) { |l, task| l << task }
puts list
p list
list.each.with_index(1) { |task, n| puts "#{n}! #{task}" }
puts "with o: #{list.count { |t| t.include?("o") }}"
puts "first: #{list.min}"
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Compilateur Ruby en ligne