Hàm khởi tạo
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 3 trên 57.
Vì R không bao giờ kiểm tra các trường của đối tượng S3, mọi đối tượng thuộc một lớp nên được tạo bằng một hàm duy nhất, gọi là hàm khởi tạo. Theo quy ước, hàm khởi tạo cấp thấp có tên là new_ cộng với tên lớp. Hàm này nhận từng trường làm đối số, kiểm tra kiểu bằng stopifnot() rồi trả về đối tượng:
new_money <- function(amount = double(), currency = character()) {
stopifnot(is.double(amount), is.character(currency))
structure(list(amount = amount, currency = currency), class = "money")
}
m <- new_money(9.5, "EUR")
cat(m$amount, m$currency)Đầu ra:
9.5 EURstopifnot() dừng chương trình khi một trong các điều kiện không phải là TRUE, và thông báo cho biết kiểm tra nào không đạt. Chương trình phát hiện kiểu dữ liệu sai ngay khi tạo đối tượng, chứ không phải đợi đến lúc một hàm khác sử dụng đối tượng đó:
new_money <- function(amount = double(), currency = character()) {
stopifnot(is.double(amount), is.character(currency))
structure(list(amount = amount, currency = currency), class = "money")
}
m <- new_money("9.5", "EUR")Đầu ra:
Error in new_money("9.5", "EUR") : is.double(amount) is not TRUE
Calls: new_money -> stopifnot
Execution haltedHàm khởi tạo là nơi duy nhất gọi structure() cho lớp, vì vậy mọi đối tượng đều có các trường giống nhau theo cùng một thứ tự. Code cần nhiều đối tượng sẽ gọi hàm này trong vòng lặp hoặc dùng lapply():
new_point <- function(x = 0, y = 0) {
stopifnot(is.numeric(x), is.numeric(y))
structure(list(x = x, y = y), class = "point")
}
pts <- lapply(1:3, function(i) new_point(i, i * i))
for (p in pts) cat(p$x, p$y, "\n")Đầu ra:
1 1
2 4
3 9 Một đối tượng S3 không nhất thiết phải là danh sách. Hàm khởi tạo có thể gắn nhãn cho một vector thông thường và lưu thêm thông tin dưới dạng thuộc tính, giống như cách các factor và ngày tháng của R hoạt động. attr(x, "unit") đọc một thuộc tính:
new_distance <- function(value = double(), unit = "m") {
stopifnot(is.double(value), is.character(unit))
structure(value, unit = unit, class = "distance")
}
d <- new_distance(c(1.5, 2), "km")
print(attr(d, "unit"))
print(unclass(d)[2] * 1000)Đầu ra:
[1] "km"
[1] 2000Thử thách
DễHoàn thành hàm khởi tạo new_book(title, author, pages) trong Book.R. Hàm này dùng stopifnot() để kiểm tra title và author có kiểu ký tự, còn pages có kiểu số, rồi trả về một danh sách gồm ba trường đó và lớp book. Sau đó, hoàn thành book_label(b), hàm này trả về Dune by Frank Herbert, 412 pages.
Code được cung cấp đọc các dòng có dạng title;author;pages. Code truyền pages dưới dạng số nếu giá trị này chỉ gồm các chữ số, nếu không thì truyền nguyên văn bản ban đầu, in ra book: cùng nhãn cho mỗi đối tượng, và in ra rejected: cùng tiêu đề khi hàm khởi tạo dừng lại.
Code của bạn nằm trong Book.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("Book.R")
# Đoạn code đầu vào/đầu ra đã cung cấp: giữ nguyên
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
p <- strsplit(line, ";")[[1]]
pages <- if (grepl("^[0-9]+$", p[3])) as.numeric(p[3]) else p[3]
b <- tryCatch(new_book(p[1], p[2], pages), error = function(e) NULL)
if (is.null(b)) {
cat("rejected: ", p[1], "\n", sep = "")
} else {
cat(class(b), ": ", book_label(b), "\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