Menu
Coddy logo textTech

Модуль Enumerable

Часть раздела Объектно-ориентированное программирование путешествия по Ruby на Coddy. Урок 30 из 57.

Enumerable — это модуль, лежащий в основе методов коллекций массивов и хешей. Класс, который определяет each, возвращая свои элементы по одному, и подключает Enumerable, получает map, select, count, sort_by, min_by, sum, include? и десятки других методов:

class Playlist
  include Enumerable

  def initialize(*songs)
    @songs = songs
  end

  def each
    @songs.each { |song| yield song }
  end
end

list = Playlist.new("Intro", "Dune", "Outro")
puts list.map(&:upcase).join(", ")
puts list.select { |s| s.size > 4 }.inspect
puts list.count
puts list.include?("Dune")

Вывод:

INTRO, DUNE, OUTRO
["Intro", "Outro"]
3
true

yield передаёт значение блоку, который вызывающий код передал в each. Класс определяет, что считать одним элементом: например, строку текста, пару значений или один из собственных объектов:

class Inventory
  include Enumerable

  def initialize
    @stock = { "apple" => 3, "pear" => 0, "plum" => 7 }
  end

  def each
    @stock.each { |name, qty| yield name, qty }
  end
end

inv = Inventory.new
p inv.select { |_, qty| qty.positive? }.map(&:first)
p inv.max_by { |_, qty| qty }

Вывод:

["apple", "plum"]
["plum", 7]

Элементы не обязательно хранить. each может вычислять их по мере необходимости, а такие методы, как first, досрочно останавливают цикл:

class Squares
  include Enumerable

  def initialize(limit)
    @limit = limit
  end

  def each
    n = 1
    while n * n <= @limit
      yield n * n
      n += 1
    end
  end
end

p Squares.new(50).to_a
p Squares.new(1000).first(3)
puts Squares.new(100).sum

Вывод:

[1, 4, 9, 16, 25, 36, 49]
[1, 4, 9]
385

Когда элементы определяют <=>, методы сортировки Enumerable, такие как sort, min и max, работают без блока. В противном случае они принимают блок, как в sort_by и min_by:

class Team
  include Enumerable

  def each(&block)
    @players.each(&block)     # передать блок напрямую
  end
end

team.sort_by(&:age)
team.min_by(&:score)
challenge icon

Задание

Легко

Дополните класс Playlist. Он содержит объекты Song (с полями title и seconds), добавленные с помощью add, подключает Enumerable и определяет each, который передаёт в блок каждую песню в порядке добавления.

Предоставленный код добавляет по одной песне для каждой входной строки (title,seconds), а затем использует только методы Enumerable для плейлиста: выводит количество песен, названия, объединённые запятыми, общую длительность в формате m:ss, название самой длинной песни и названия песен продолжительностью более 3 минут, отсортированные в алфавитном порядке (или none).

Ваш код должен находиться в song.rb и playlist.rb. Файл main.rb содержит предоставленный код ввода/вывода, и его нельзя редактировать.

Попробуйте сами

require_relative 'song'
require_relative 'playlist'

# Предоставленный код ввода/вывода: оставьте его как есть
input = []
while (line = gets)
  input << line.chomp
end
list = Playlist.new
input.each do |line|
  title, seconds = line.split(",")
  list.add(Song.new(title, seconds.to_i))
end
total = list.sum(&:seconds)
long = list.select { |s| s.seconds > 180 }.map(&:title).sort
puts "songs: #{list.count}"
puts "titles: #{list.map(&:title).join(", ")}"
puts format("total: %d:%02d", total / 60, total % 60)
puts "longest: #{list.max_by(&:seconds).title}"
puts "over 3 min: #{long.empty? ? "none" : long.join(", ")}"
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Ruby