Menu
CoddyTech

Mẫu Iterator

Thuộc phần Lập trình hướng đối tượng trong hành trình R của Coddy. Bài học 49 trên 57.

Một bộ lặp lần lượt cung cấp từng phần tử của một tập hợp, nên đoạn code sử dụng các phần tử đó không cần biết tập hợp được lưu trữ như thế nào. Một bao đóng giữ vị trí; has_next() và next_value() là toàn bộ giao diện:

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

Đầu ra:

a b c 

Bộ sinh (generator) là một trình lặp tính từng giá trị khi được yêu cầu, nên dãy không cần tồn tại trong bộ nhớ. Dãy thậm chí có thể kéo dài vô tận:

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

Đầu ra:

0 1 1 2 3 5 8 13 21 34 

Bộ lặp ẩn đi cách sắp xếp. Các kết quả được lưu trong những trang, một số trang trống, sẽ được trả ra thành một dãy phẳng duy nhất:

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

Đầu ra:

a b c 

Các hàm trợ giúp được viết dựa trên giao diện sẽ hoạt động với mọi trình lặp. take(it, n) lấy tối đa n giá trị từ một vector, một hàm sinh hoặc bất kỳ thứ gì khác có has_next() và 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))

Đầu ra:

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

Thử thách

Dễ

Hoàn thành Iterator.R:

  • make_iterator(x): một bộ lặp qua các phần tử của một vectơ, có has_next() và next_value();
  • make_range(from, to, by): một bộ tạo các giá trị from, from + by, ... cho đến và bao gồm to, được tính lần lượt từng giá trị;
  • take(it, n): lấy tối đa n giá trị tiếp theo từ bất kỳ bộ lặp nào.

Code được cung cấp đọc các dòng range 1 10 3 take 2 5 (from, to, by, sau đó là kích thước của mỗi lần take) hoặc list a,b,c take 2 2, rồi in ra giá trị mà mỗi take() trả về.

Code của bạn nằm trong Iterator.R. main.R chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa tệp này.

Tự làm thử

source("Iterator.R")

# Đoạn code đầu vào/đầu ra đã cho: giữ nguyên
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 iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch R online