类向量
本课属于 Coddy R 学习旅程的“面向对象编程”单元。第 12 课,共 57 课。
S3 继承来自类向量。一个具有 class = c("manager", "employee") 的对象既是经理,也是员工。如果泛型没有针对 manager 的方法,分派就会继续查找,并使用 employee 方法:
pay <- function(x) UseMethod("pay")
pay.employee <- function(x) x$salary / 12
m <- structure(list(name = "Ada", salary = 60000), class = c("manager", "employee"))
print(class(m))
print(pay(m))输出:
[1] "manager" "employee"
[1] 5000在每个构造函数中手动编写类向量,会重复父类的详细信息。父类构造函数会接收一个 class 参数,并将它放在自身类的前面,而子类构造函数会调用父类构造函数:
new_employee <- function(name, salary, ..., class = character()) {
structure(list(name = name, salary = salary, ...), class = c(class, "employee"))
}
new_manager <- function(name, salary, reports) {
new_employee(name, salary, reports = reports, class = "manager")
}
m <- new_manager("Ada", 60000, 4)
print(class(m))
print(names(m))输出:
[1] "manager" "employee"
[1] "name" "salary" "reports"父类构造函数中的 ... 会收集子类的额外字段,这里是 reports,并将它们放在父类自己的字段之后。普通员工完全没有 reports 字段:
new_employee <- function(name, salary, ..., class = character()) {
structure(list(name = name, salary = salary, ...), class = c(class, "employee"))
}
e <- new_employee("Bo", 40000)
print(class(e))
print(names(e))输出:
[1] "employee"
[1] "name" "salary"类向量的顺序很重要:最具体的类要放在最前面。如果父类在前,它的方法会优先使用,子类的方法则永远不会被使用:
title <- function(x) UseMethod("title")
title.employee <- function(x) "employee"
title.manager <- function(x) "manager"
right <- structure(list(), class = c("manager", "employee"))
wrong <- structure(list(), class = c("employee", "manager"))
print(title(right))
print(title(wrong))输出:
[1] "manager"
[1] "employee"挑战
简单员工和经理的代码分别放在两个文件中。请在 Employee.R 中完成 new_employee(name, salary, ..., class = character()):它会返回一个包含 name、salary 以及从 ... 传入的所有额外字段的列表,并使用类向量 c(class, "employee")。然后在 Manager.R 中完成 new_manager(name, salary, reports),它会调用 new_employee(),传入额外字段 reports 和类 manager。
已提供 label() 和它的 employee 方法,因此经理也能获得标签,无需为经理单独编写方法。提供的代码会读取 employee Bo 40000 或 manager Ada 60000 4 这样的行,并输出类向量、字段名称和标签。
你的代码应写在 Employee.R 和 Manager.R 中。main.R 包含提供的输入和输出代码,不能编辑。
亲手试试
source("Employee.R")
source("Manager.R")
# 提供的输入和输出代码:保持原样
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
p <- strsplit(line, " ")[[1]]
x <- if (p[1] == "manager") new_manager(p[2], as.numeric(p[3]), as.numeric(p[4])) else new_employee(p[2], as.numeric(p[3]))
cat("class: ", paste(class(x), collapse = ", "), "\n", sep = "")
cat("fields: ", paste(names(x), collapse = ", "), "\n", sep = "")
cat(label(x), "\n", sep = "")
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: R 在线编译器