Menu
CoddyTech

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 EUR

stopifnot() 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 halted

Hà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] 2000
challenge icon

Thử 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 = "")
  }
}
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