Menu
CoddyTech

创建泛型

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

S4 泛型通过 setGeneric() 创建。它的函数体是 standardGeneric("name"),相当于 S4 中的 UseMethod()。调用 setGeneric() 会返回泛型的名称,在顶层调用时会打印该名称:

setGeneric("area", function(shape) standardGeneric("area"))

输出:

[1] "area"

将调用包装在 invisible() 中,这样这一行就不会出现在输出中。接着,setMethod("area", "Circle", ...) 会为一个类添加方法:

setClass("Circle", slots = c(r = "numeric"))
setClass("Square", slots = c(side = "numeric"))
invisible(setGeneric("area", function(shape) standardGeneric("area")))
setMethod("area", "Circle", function(shape) pi * shape@r^2)
setMethod("area", "Square", function(shape) shape@side^2)
cat(area(new("Circle", r = 1)), area(new("Square", side = 3)))

输出:

3.141593 9

方法的参数与其泛型相同。泛型有 ... 时,方法可以在它们后面添加自己的命名参数:

setClass("Circle", slots = c(r = "numeric"))
invisible(setGeneric("describe", function(x, ...) standardGeneric("describe")))
setMethod("describe", "Circle", function(x, ..., digits = 2) {
  paste("circle of area", round(pi * x@r^2, digits))
})
c1 <- new("Circle", r = 2)
print(describe(c1))
print(describe(c1, digits = 4))

输出:

[1] "circle of area 12.57"
[1] "circle of area 12.5664"

setMethod() 需要泛型已经存在,因此先加载包含 setGeneric() 的文件。对没有方法的类进行调用时,会报错并指出签名:

invisible(setGeneric("area", function(shape) standardGeneric("area")))
setClass("Circle", slots = c(r = "numeric"))
setMethod("area", "Circle", function(shape) pi * shape@r^2)
area(42)

输出:

Error: unable to find an inherited method for function ‘area’ for signature ‘shape = "numeric"’
Execution halted
challenge icon

挑战

简单

图形是分布在三个文件中的 S4 类。在 Shape.R 中,用 setGeneric() 创建泛型 area(shape) 和 perimeter(shape),并用 invisible() 包装。在 Circle.R 和 Square.R 中声明类,并为每个类添加 area 和 perimeter 方法,使用 setMethod()。

提供的代码会读取 circle 2 或 square 3 这样的行,并打印 circle: area 12.57, perimeter 12.57。

你的代码写在 Shape.R、Circle.R 和 Square.R 中。main.R 包含提供的输入/输出代码,不能编辑。

亲手试试

source("Shape.R")
source("Circle.R")
source("Square.R")

# 已提供的输入/输出代码:保持原样
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  p <- strsplit(line, " ")[[1]]
  s <- if (p[1] == "circle") new("Circle", r = as.numeric(p[2])) else new("Square", side = as.numeric(p[2]))
  cat(sprintf("%s: area %.2f, perimeter %.2f", p[1], area(s), perimeter(s)), "\n", sep = "")
}
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: R 在线编译器