Ruby

RubyでEnumerable.eachの進捗を表示するメソッドを実装

こういうの
each_with_progress

環境

  • Ruby 2.5.1

実装

進捗を標準出力に表示するEnumerable.each_with_progressメソッドを、新たに追加します。

index.rb
module Enumerable
  def each_with_progress(&block)
    self.each_with_index do |element, index|
      print "\r#{index + 1}/#{self.length}"
      block.call(element, index)
    end
    print "\r\n"
  end
end

print文を変更すれば、割合表示にしたり色々できると思います。

呼び出し側のコード

index.rb
puts "# program start"

['a','b','c'].each_with_progress do |elem|
  sleep 1
  print " #{elem}"
end

['a','b','c'].each_with_progress do |elem, index|
  sleep 1
  print " #{index} -> #{elem}"
end

puts "# program end"

実行結果

$ ruby index.rb
# program start
3/3 c
3/3 2 -> c
# program end

Enumerableで実行可能にする

上記の例は、配列などlengthメソッドが定義されているクラスでしか使えません。

Enumerableでlengthが実装されていないクラス(例えば範囲オブジェクトなど)では、lengthの代わりにEnumerable.countを使います。

index.rb
module Enumerable
  def each_with_progress(&block)
    length = self.respond_to?(:length) ? self.length : self.count

    self.each_with_index do |element, index|
      print "\r#{index + 1}/#{length}"
      block.call(element, index)
    end
    print "\r\n"
  end
end

こうすることで、Enumerableモジュールを継承しているオブジェクトで実行可能になります。

呼び出し側のコード

index.rb
puts "# program start"

sum = 0
(1..10000).each_with_progress do |elem|
  sum += elem
end
puts sum

puts "# program end"

実行結果

$ ruby index.rb
# program start
10000/10000
50005000
# program end

参考