Menu
CoddyTech

وحدة 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 عبر الإنترنت