迭代器模式
本课属于 Coddy R 学习旅程的“面向对象编程”单元。第 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挑战
简单完成 Iterator.R:
make_iterator(x):创建一个用于遍历向量元素的迭代器,包含has_next()和next_value();make_range(from, to, by):逐个生成从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 = "")
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: R 在线编译器