Menu
CoddyTech

NextMethod 基础

本课属于 Coddy R 学习旅程的“面向对象编程”单元。第 10 课,共 57 课。

在方法内部,NextMethod() 会使用相同的参数,调用同一个泛型对应的下一个方法。对于只有一个类的对象,下一个方法是默认方法,因此某个方法可以复用通用答案并在此基础上补充内容:

describe <- function(x) UseMethod("describe")
describe.default <- function(x) paste("an object with", length(x), "fields")
describe.task <- function(x) paste0(NextMethod(), ", titled ", x$title)
t <- structure(list(title = "Buy milk", done = FALSE), class = "task")
print(describe(t))

输出:

[1] "an object with 2 fields, titled Buy milk"

一个对象在向量中可以有多个类,例如 c("urgent", "task")。分派会按顺序尝试这些类,而第一个方法中的 NextMethod() 会转到下一个类对应的方法:

describe <- function(x) UseMethod("describe")
describe.task <- function(x) paste("task:", x$title)
describe.urgent <- function(x) paste("URGENT", NextMethod())
fix <- structure(list(title = "Fix the leak"), class = c("urgent", "task"))
print(describe(fix))

输出:

[1] "URGENT task: Fix the leak"

NextMethod() 会传递方法接收到的每个参数,包括 sep 这样的额外参数。下一个方法会看到相同的值,你不必再次列出它们:

label <- function(x, sep = ": ") UseMethod("label")
label.default <- function(x, sep = ": ") paste0("item", sep, x$name)
label.box <- function(x, sep = ": ") paste0("[", NextMethod(), "]")
b <- structure(list(name = "tools"), class = "box")
print(label(b))
print(label(b, sep = " = "))

输出:

[1] "[item: tools]"
[1] "[item = tools]"

Base R 使用 NextMethod(),在对向量取子集时保留其类。默认的 [ 会丢弃类,因此可以使用 "[.percent" 方法执行操作并恢复类:

format.percent <- function(x, ...) paste0(unclass(x) * 100, "%")
"[.percent" <- function(x, i) structure(NextMethod(), class = "percent")
p <- structure(c(0.1, 0.25, 0.5), class = "percent")
print(format(p[2:3]))

输出:

[1] "25%" "50%"
challenge icon

挑战

简单

任务和紧急任务是定义在两个文件中的两个类。在 Task.R 中补全泛型函数 describe()、返回 an item 的默认方法,以及 describe.task()。后者通过调用 NextMethod() 并添加标题,返回 an item: Buy milk。在 Urgent.R 中补全 describe.urgent(),它通过调用 NextMethod() 返回 URGENT an item: Fix bug。

构造函数已提供:紧急任务的类是 c("urgent", "task")。提供的代码会读取 task Buy milk、urgent Fix bug 或其他带标题的单词,将其转换为该类的对象,并为每个对象打印 describe() 的结果。

你的代码写在 Task.R 和 Urgent.R 中。main.R 中是提供的输入/输出代码,不能编辑。

亲手试试

source("Task.R")
source("Urgent.R")

# 提供的输入/输出代码:保持原样。
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  kind <- sub(" .*", "", line)
  title <- sub("^[a-z]+ ", "", line)
  t <- if (kind == "urgent") new_urgent(title) else if (kind == "task") new_task(title) else structure(list(title = title), class = kind)
  cat(describe(t), "\n", sep = "")
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: R 在线编译器