Menu
CoddyTech

Closure dưới dạng đối tượng

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 17 trên 57.

Bạn đã học về closure trong Logic & Flow: một hàm được tạo bên trong một hàm khác sẽ giữ lại các biến xung quanh nó. Hàm tạo hàm trả về một danh sách các hàm như vậy sẽ tạo ra một đối tượng. Các biến cục bộ của nó là dữ liệu của đối tượng, còn các hàm là phương thức của đối tượng:

make_account <- function(balance = 0) {
  deposit <- function(x) balance <<- balance + x
  get_balance <- function() balance
  list(deposit = deposit, get_balance = get_balance)
}
acc <- make_account(100)
acc$deposit(50)
print(acc$get_balance())

Đầu ra:

[1] 150

Mỗi lần gọi hàm tạo, phần thân của nó lại chạy, vì vậy mỗi đối tượng có các biến riêng. Thay đổi tài khoản này không ảnh hưởng đến tài khoản kia:

make_account <- function(balance = 0) {
  list(deposit = function(x) balance <<- balance + x,
       get_balance = function() balance)
}
ada <- make_account(100)
bo <- make_account(5)
ada$deposit(1)
cat(ada$get_balance(), bo$get_balance())

Đầu ra:

101 5

Dữ liệu là riêng tư. balance không phải là phần tử của danh sách, nên acc$balance là NULL, và việc gán giá trị cho nó chỉ thêm một phần tử không liên quan. Các hàm trong danh sách tạo nên toàn bộ giao diện công khai của đối tượng:

make_account <- function(balance = 0) {
  list(get_balance = function() balance)
}
acc <- make_account(100)
print(acc$balance)
acc$balance <- 1e6
print(acc$get_balance())

Đầu ra:

NULL
[1] 100

Các phương thức có thể gọi lẫn nhau nếu chúng được định nghĩa dưới dạng hàm cục bộ trước khi tạo danh sách. Ở đây, add_many() dùng lại add():

make_tally <- function() {
  total <- 0
  add <- function(x) total <<- total + x
  add_many <- function(xs) for (x in xs) add(x)
  get <- function() total
  list(add = add, add_many = add_many, get = get)
}
t <- make_tally()
t$add(1)
t$add_many(c(2, 3, 4))
print(t$get())

Đầu ra:

[1] 10
challenge icon

Thử thách

Dễ

Hoàn thiện hàm tạo make_counter(start = 0) trong Counter.R. Hàm này giữ giá trị hiện tại ở phạm vi riêng tư và trả về một danh sách gồm bốn hàm: increment() và decrement() thay đổi giá trị đi 1, reset() đặt giá trị về start, còn value() trả về giá trị đó.

Code được cung cấp đọc giá trị bắt đầu, sau đó đọc các lệnh +, - hoặc reset, rồi in ra giá trị sau mỗi lệnh. Cuối cùng, code in ra tên các phần tử trong danh sách được trả về, danh sách này chỉ được chứa bốn hàm.

Đặt code của bạn trong Counter.R. Tệp 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("Counter.R")

# Đoạn code đầu vào/đầu ra có sẵn: giữ nguyên
input <- suppressWarnings(readLines(file("stdin")))
counter <- make_counter(as.numeric(input[1]))
for (cmd in input[-1]) {
  if (cmd == "+") counter$increment()
  if (cmd == "-") counter$decrement()
  if (cmd == "reset") counter$reset()
  cat(cmd, " -> ", counter$value(), "\n", sep = "")
}
cat("fields visible: ", paste(sort(names(counter)), 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