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] 150Mỗ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 5Dữ 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] 100Cá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] 10Thử 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 = "")
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
1Kiến thức cơ bản về S3
Làm việc với tệpDanh sách với một lớpHàm khởi tạoPhương thức in ra và định dạngTrình xác thực và hàm hỗ trợÔn tập: nhiệt độ4Tính đóng gói trong R
Closure dưới dạng đối tượngMôi trường dưới dạng đối tượngHàm truy cậpBảo vệ trạng tháiÔn tập: đồng hồ tính phí đỗ xe2S3, hàm generic và phương thức
Hàm generic và UseMethodPhương thức mặc địnhPhương thức cho hàm generic cơ sởCơ bản về NextMethodÔn tập: diện tích hình5Các lớp S4
Định nghĩa lớp S4Slot và quyền truy cậpKiểm tra tính hợp lệPhương thức showÔn tập: Danh mục sản phẩmTự luyện tập: Trình biên dịch R online