Menu
Coddy logo textTech

Iterator-Muster

Teil des Abschnitts Objektorientierte Programmierung der R-Journey von Coddy. Lektion 49 von 57.

Ein Iterator gibt die Elemente einer Sammlung einzeln aus, sodass der Code, der sie verwendet, nicht wissen muss, wie die Sammlung gespeichert ist. Eine Closure merkt sich die Position; has_next() und next_value() bilden die gesamte Schnittstelle:

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(), "")

Ausgabe:

a b c 

Ein Generator ist ein Iterator, der jeden Wert berechnet, wenn er angefordert wird, sodass die Sequenz nicht im Speicher vorhanden sein muss. Er kann sogar endlos sein:

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(), "")

Ausgabe:

0 1 1 2 3 5 8 13 21 34 

Der Iterator verbirgt das Layout. In Seiten gespeicherte Ergebnisse, von denen einige leer sind, werden als eine flache Sequenz ausgegeben:

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(), "")

Ausgabe:

a b c 

Helfer, die für die Schnittstelle geschrieben wurden, funktionieren mit jedem Iterator. take(it, n) ruft bis zu n Werte ab – aus einem Vektor, einem Generator oder aus allem anderen mit has_next() und 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))

Ausgabe:

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

Aufgabe

Einfach

Vervollständige Iterator.R:

  • make_iterator(x): ein Iterator über die Elemente eines Vektors mit has_next() und next_value();
  • make_range(from, to, by): ein Generator für from, from + by, ... bis einschließlich to, wobei die Werte einzeln berechnet werden;
  • take(it, n): bis zu n nächste Werte eines beliebigen Iterators.

Der bereitgestellte Code liest Zeilen wie range 1 10 3 take 2 5 (from, to, by, dann die Größen jedes take) oder list a,b,c take 2 2 und gibt aus, was jeder Aufruf von take() zurückgibt.

Dein Code gehört in Iterator.R. main.R enthält den bereitgestellten Eingabe-/Ausgabecode und kann nicht bearbeitet werden.

Probier es selbst

source("Iterator.R")

# Vorgegebener Ein-/Ausgabecode: belasse ihn wie er ist
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 iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-R-Compiler