Menu
Coddy logo textTech

Enumerableモジュール

CoddyのRubyジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 30/57。

Enumerable は、配列とハッシュのコレクションメソッドを支えるモジュールです。要素を1つずつ返す 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に渡されたブロックに値を渡します。クラスは、テキストの1行、値のペア、または独自のオブジェクトの1つなど、1つの要素が何であるかを決定します。

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

要素が <=> を定義している場合、sort、min、max などの Enumerable のソートメソッドはブロックなしで動作します。それ以外の場合は、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 クラスを完成させてください。このクラスは、add で追加された Song オブジェクト(title と seconds を持つ)を保持し、Enumerable をインクルードして、追加された順番にすべての曲を yield する each を定義します。

用意されたコードは、入力の各行(title,seconds)ごとに 1 曲を追加し、その後プレイリストに対して Enumerable のメソッドだけを使用します。曲数、コンマで連結したタイトル、合計時間を m:ss 形式で表示し、最も長い曲のタイトルと、3 分を超える曲のタイトルをアルファベット順に並べて表示します(または none)。

コードは song.rb と playlist.rb に記述してください。main.rb には用意された入力/出力コードが含まれており、編集できません。

REQUIRED OUTPUT FORMAT: [Your translated content here]

自分で試してみよう

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オンラインコンパイラ