Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نمط المكرّر

جزء من قسم البرمجة كائنية التوجه في رحلة R على Coddy. الدرس 49 من 57.

يقوم المكرّر بتسليم عناصر مجموعة واحدًا تلو الآخر، لذلك لا يحتاج الكود الذي يستخدمها إلى معرفة كيفية تخزين المجموعة. يحتفظ الإغلاق بالموضع؛ ويمثّل كلٌّ من has_next() وnext_value() الواجهةَ كاملةً:

make_iterator <- function(x) {
  i <- 0
  list(
    has_next = function() i < length(x),
    next_value = function() {
      i <<- i + 1
      x[[i]]
    }
  )
}
it <- make_iterator(c("a", "b", "c"))
while (it$has_next()) cat(it$next_value(), "")

الناتج:

a b c 

إن المولِّد هو مكرّر يحسب كل قيمة عند طلبها، ولذلك لا يتعيّن أن يكون التسلسل موجودًا في الذاكرة. ويمكن أن يكون حتى بلا نهاية:

make_fib <- function() {
  a <- 0
  b <- 1
  list(has_next = function() TRUE,
       next_value = function() {
         value <- a
         nxt <- a + b
         a <<- b
         b <<- nxt
         value
       })
}
fib <- make_fib()
for (i in 1:10) cat(fib$next_value(), "")

الناتج:

0 1 1 2 3 5 8 13 21 34 

يخفي المُكرّر البنية. تخرج النتائج المخزّنة في صفحات، بعضها فارغ، كتسلسل مسطّح واحد:

make_paged <- function(pages) {
  p <- 1
  i <- 0
  skip_empty <- function() while (p <= length(pages) && i >= length(pages[[p]])) { p <<- p + 1; i <<- 0 }
  list(
    has_next = function() { skip_empty(); p <= length(pages) },
    next_value = function() { skip_empty(); i <<- i + 1; pages[[p]][i] }
  )
}
it <- make_paged(list(c("a", "b"), character(0), c("c")))
while (it$has_next()) cat(it$next_value(), "")

الناتج:

a b c 

تعمل الأدوات المساعدة المكتوبة وفقًا للواجهة مع كل مُكرِّر. تستخرج take(it, n) ما يصل إلى n من القيم، من متجه أو مولِّد أو أي شيء آخر يحتوي على has_next() وnext_value():

make_iterator <- function(x) {
  i <- 0
  list(has_next = function() i < length(x), next_value = function() { i <<- i + 1; x[[i]] })
}
take <- function(it, n) {
  out <- c()
  while (length(out) < n && it$has_next()) out <- c(out, it$next_value())
  out
}
it <- make_iterator(1:5)
print(take(it, 2))
print(take(it, 10))

الناتج:

[1] 1 2
[1] 3 4 5
challenge icon

التحدي

سهل

أكمل Iterator.R:

  • make_iterator(x): مكرّر على عناصر متجه، مع has_next() وnext_value()؛
  • make_range(from, to, by): مولّد للقيم from وfrom + by ... حتى to بشكل شامل، مع حساب كل قيمة على حدة؛
  • take(it, n): ما يصل إلى n من القيم التالية لأي مكرّر.

يقرأ الكود المزوّد الأسطر range 1 10 3 take 2 5 (from وto وby، ثم أحجام كل عملية take) أو list a,b,c take 2 2، ويطبع ما تُرجعه كل عملية take().

ضع الكود الخاص بك في Iterator.R. يحتوي main.R على كود الإدخال/الإخراج المزوّد ولا يمكن تعديله.

جرّب بنفسك

source("Iterator.R")

# كود الإدخال/الإخراج المُزوَّد: أبقِه كما هو
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  p <- strsplit(line, " ")[[1]]
  it <- if (p[1] == "range") make_range(as.numeric(p[2]), as.numeric(p[3]), as.numeric(p[4])) else make_iterator(strsplit(p[2], ",")[[1]])
  k <- if (p[1] == "range") 5 else 3
  for (n in as.numeric(p[-(1:k)])) {
    got <- take(it, n)
    cat("take ", n, ": ", if (length(got) == 0) "(nothing)" else paste(got, collapse = " "), "\n", sep = "")
  }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم R عبر الإنترنت