Menu
CoddyTech

Closure có trạng thái

Thuộc phần Logic và luồng điều khiển trong hành trình R của Coddy. Bài học 30 trên 64.

Bên trong một hàm, <- tạo một biến cục bộ. Toán tử <<- thay vào đó gán giá trị cho biến cùng tên trong hàm bao quanh, nhờ vậy một closure có thể cập nhật giá trị mà nó đã nắm giữ:

make_counter <- function() {
  count <- 0
  function() {
    count <<- count + 1
    count
  }
}
next_id <- make_counter()
print(next_id())
print(next_id())
print(next_id())

Đầu ra:

[1] 1
[1] 2
[1] 3

Mỗi bộ đếm do hàm tạo tạo ra có count riêng, nên chúng không bao giờ ảnh hưởng lẫn nhau:

make_counter <- function() {
  count <- 0
  function() {
    count <<- count + 1
    count
  }
}
a <- make_counter()
b <- make_counter()
for (i in 1:2) a()
print(a())
print(b())

Đầu ra:

[1] 3
[1] 1

Với <- thông thường bên trong hàm đóng, phép cập nhật tạo một biến cục bộ mới và biến đó biến mất sau mỗi lần gọi, còn giá trị được lưu trữ không bao giờ thay đổi:

make_broken <- function() {
  count <- 0
  function() {
    count <- count + 1
    count
  }
}
f <- make_broken()
for (i in 1:2) f()
print(f())

Đầu ra:

[1] 1

Hàm tạo có thể trả về một danh sách các hàm đóng (closure) dùng chung các biến. Khi kết hợp lại, chúng hoạt động như một đối tượng nhỏ có dữ liệu riêng:

make_wallet <- function(balance) {
  list(
    add = function(x) balance <<- balance + x,
    get = function() balance
  )
}
w <- make_wallet(10)
w$add(5)
print(w$get())

Đầu ra:

[1] 15
challenge icon

Thử thách

Dễ

Hoàn thiện make_counter(). Hàm này trả về một hàm, mỗi lần được gọi sẽ tăng biến đếm riêng lên 1 và trả về số đếm mới, bắt đầu từ 1.

Code được cung cấp tạo hai bộ đếm, a và b. Code đọc một danh sách a và b được phân tách bằng dấu phẩy, gọi bộ đếm tương ứng cho mỗi mục và in từng giá trị trả về trên một dòng riêng.

Tự làm thử

make_counter <- function() {
  # Viết code của bạn ở đây
  function() 0
}

# Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
input <- suppressWarnings(readLines(file("stdin")))
counters <- list(a = make_counter(), b = make_counter())
for (k in strsplit(input[1], ",")[[1]]) cat(counters[[k]](), "\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 Logic và luồng điều khiển

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