Menu
Coddy logo textTech

Iteratorパターン

CoddyのRジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 49/57。

イテレーターはコレクションの項目を1つずつ取り出すため、項目を使うコードはコレクションがどのように格納されているかを知る必要がありません。クロージャーが位置を保持し、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 

イテレータはレイアウトを隠します。いくつかは空のページに格納された結果も、1つのフラットなシーケンスとして取り出されます。

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)は、ベクター、ジェネレーター、またはhas_next()とnext_value()を備えたその他のものから、最大でn個の値を取り出します。

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まで(toを含む)の値を、一度に1つずつ計算して生成するジェネレーター;
  • 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オンラインコンパイラ