التابع Enumerable.slice_before في روبي

من موسوعة حسوب
مراجعة 18:01، 30 ديسمبر 2018 بواسطة جميل-بيلوني (نقاش | مساهمات) (إنشاء الصفحة.)
(فرق) → مراجعة أقدم | المراجعة الحالية (فرق) | مراجعة أحدث ← (فرق)

يقطِّع التابع slice_before عناصر الكائن القابل للتعداد إلى أجزاء تحدَّد بدايتها عبر نمطٍ محدِّدٍ أو كتلةٍ معطاةٍ ثم يعيد مُعدِّد لكل جزء من الأجزاء الناتجة.

البنية العامة

slice_before(pattern)  an_enumerator
slice_before { |elt| bool }  an_enumerator

إن كان النمط مطابقًا للعنصر (أي يعيد pattern === elt القيمة true) أو أعادت الكتلة القيمة true للعنصر المُمرَّر إليها، فسيُعدُّ العنصر بدايةً لجزء. يُستدعَى المعامل === والكتلة block من أول عنصر وحتى آخر عنصر من عناصر الكائن القابل للتعداد المعطى. يجري دومًا تجاهل النتيجة المعادة لأول عنصر. يُنتِج المُعدِّد المعاد العناصر المُجزَّأة كمصفوفة. لذلك، يمكن استدعاء التابع each بالشكل التالي:

enum.slice_before(pattern).each { |ary| ... }
enum.slice_before { |elt| bool }.each { |ary| ... }

يمكن أيضًا استعمال توابع الصنف Enumerator والوحدة Enumerable مثل التابع map وغيره.

المعاملات

pattern

النمط المراد مطابقته للعناصر لتحديد نهايات الأجزاء المراد تقطيع عناصر الكائن المعطى إليها.

القيمة المعادة

يعاد مُعدِّد لكل جزء من الأجزاء المُقطَّعة.

أمثلة

مثال على التكرار عبر مدخلات ChangeLog باستعمال التابع slice_before:

open("ChangeLog") { |f|
  f.slice_before(/\A\S/).each { |e| pp e }
}

# same as above.  block is used instead of pattern argument.
open("ChangeLog") { |f|
  f.slice_before { |line| /\A\S/ === line }.each { |e| pp e }
}

يُنتِج "svn proplist -R" مخرجات متعددة الأسطر لكل ملف. يمكن تقطيعهم بالشكل التالي:

IO.popen([{"LC_ALL"=>"C"}, "svn", "proplist", "-R"]) { |f|
  f.lines.slice_before(/\AProp/).each { |lines| p lines }
}
#=> ["Properties on '.':\n", "  svn:ignore\n", "  svk:merge\n"]
#   ["Properties on 'goruby.c':\n", "  svn:eol-style\n"]
#   ["Properties on 'complex.c':\n", "  svn:mime-type\n", "  svn:eol-style\n"]
#   ["Properties on 'regparse.c':\n", "  svn:eol-style\n"]
#   ...

إن احتاجت الكتلة الحفاظ على عناصر متعددة، فيمكن آنذاك استعمال متغيرات محلية. على سبيل المثال، يمكن أن تُجمَّع ثلاثة أعداد متزايدة ومتتالية أو أكثر في أجزاء بالشكل التالي (اطلع على توثيق التابع chunk_while، إذ توفر طريقة أفضل لأداء المهمة نفسها):

a = [0, 2, 3, 4, 6, 7, 9]
prev = a[0]
p a.slice_before { |e|
  prev, prev2 = e, prev
  prev2 + 1 != e
}.map { |es|
  es.length <= 2 ? es.join(",") : "#{es.first}-#{es.last}"
}.join(",")
#=> "0,2-4,6,7,9"

على أي حال، يجب استعمال متغيرات محلية بحذر إن كان المُعدِّد الناتج يكرَّر (يُعدَّد [enumerate]) مرتين أو أكثر. يجب أن تُهيَّأ المتغيرات المحلية لكل عملية تكرار (تعداد [enumeration]). يمكن استعمال التابع Enumerator.new لأداء ذلك.

# التفاف الكلمة. هذا يفترض أن المحارف لها العرض نفسه
def wordwrap(words, maxwidth)
  Enumerator.new {|y|
    # Enumerator.new في cols جرى تهيئة
    cols = 0
    words.slice_before { |w|
      cols += 1 if cols != 0
      cols += w.length
      if maxwidth < cols
        cols = w.length
        true
      else
        false
      end
    }.each {|ws| y.yield ws }
  }
end
text = (1..20).to_a.join(" ")
enum = wordwrap(text.split(/\s+/), 10)
puts "-"*10
enum.each { |ws| puts ws.join(" ") } # أول تكرار
puts "-"*10
enum.each { |ws| puts ws.join(" ") } # ولد التكرار الثاني نفس النتيجة التي ولدها الأول
puts "-"*10
#=> ----------
#   1 2 3 4 5
#   6 7 8 9 10
#   11 12 13
#   14 15 16
#   17 18 19
#   20
#   ----------
#   1 2 3 4 5
#   6 7 8 9 10
#   11 12 13
#   14 15 16
#   17 18 19
#   20
#   ----------

يحوي mbox سلسلة من البريد الإلكتروني الذي يبدأ بالسطر Unix From؛ لذلك، يكن استخراج كل بريد عبر تقطيعه إلى أجزاء باستعمال slice_before تسبق السطر Unix From:

# mbox تحليل
open("mbox") { |f|
  f.slice_before { |line|
    line.start_with? "From "
  }.each { |mail|
    unix_from = mail.shift
    i = mail.index("\n")
    header = mail[0...i]
    body = mail[(i+1)..-1]
    body.pop if body.last == "\n"
    fields = header.slice_before { |line| !" \t".include?(line[0]) }.to_a
    p unix_from
    pp fields
    pp body
  }
}

# Unix From استخراج كل بريد عبر تقطيعه إلى أجزاء تسبق السطر
open("mbox") { |f|
  emp = true
  f.slice_before { |line|
    prevemp = emp
    emp = line == "\n"
    prevemp && line.start_with?("From ")
  }.each { |mail|
    mail.pop if mail.last == "\n"
    pp mail
  }
}

انظر أيضًا

  • التابع group_by: يجمِّع عناصر الكائن القابل للتعداد الذي استدعي معه في مجموعات بحسب النتيجة المقابلة لكل عنصر التي تعيدها الكتلة المعطاة بعد تمريره إليها.
  • التابع slice_after: يقطِّع عناصر الكائن القابل للتعداد إلى أجزاء تحدَّد نهايتها عبر نمطٍ محدِّدٍ أو كتلةٍ معطاةٍ ثم يعيد مُعدِّد لكل جزء من الأجزاء الناتجة.
  • التابع slice_when: يقطِّع عناصر الكائن القابل للتعداد إلى أجزاء تحدَّد بدايتها عبر كتلةٍ معطاةٍ ثم يعيد مُعدِّد لكل جزء من الأجزاء الناتجة.

مصادر