复制语义
本课属于 Coddy R 学习旅程的“面向对象编程”单元。第 39 课,共 57 课。
S3 和 S4 对象是值:修改它们的函数只会改变自己的副本。RC 对象是引用:调用者的对象会改变。同一个函数对这两种对象的行为不同:
setClass("S4Box", slots = c(n = "numeric"))
RCBox <- setRefClass("RCBox", fields = list(n = "numeric"))
bump_s4 <- function(b) { b@n <- b@n + 1; invisible(b) }
bump_rc <- function(b) b$n <- b$n + 1
v <- new("S4Box", n = 1)
r <- RCBox$new(n = 1)
bump_s4(v)
bump_rc(r)
cat(v@n, r$n)输出:
1 2$copy() 会创建一个字段值相同的独立新对象。修改副本不会影响原对象:
Account <- setRefClass("Account", fields = list(balance = "numeric"))
a <- Account$new(balance = 100)
alias <- a
backup <- a$copy()
alias$balance <- 0
cat(a$balance, backup$balance)输出:
0 100一个列表可以多次保存同一个 RC 对象。更改其中一个元素会导致所有元素都发生变化。如果需要让元素彼此独立,就要为每个元素创建一个新对象:
Counter <- setRefClass("Counter", fields = list(n = "numeric"))
one <- Counter$new(n = 0)
same <- list(one, one, one)
same[[1]]$n <- 5
fresh <- lapply(1:3, function(i) Counter$new(n = 0))
fresh[[1]]$n <- 5
cat(same[[3]]$n, fresh[[3]]$n)输出:
5 0对于同一个 RC 对象的两个名称,identical(a, b) 的结果是 TRUE;对于副本,结果是 FALSE,即使字段仍然相等:
Account <- setRefClass("Account", fields = list(balance = "numeric"))
a <- Account$new(balance = 100)
b <- a
c2 <- a$copy()
cat(identical(a, b), identical(a, c2), c2$balance == a$balance)输出:
TRUE FALSE TRUE挑战
简单文本编辑器会保留保存点。在 Document.R 中声明引用类 Document,并添加字段 text(字符类型)和方法 type(s),将 s 追加到文本中。然后编写 checkpoint(doc),返回文档的独立副本。
包含方法的定义由 suppressMessages() 包裹。提供的代码会先创建一个保存点,然后读取命令:save 会保存 checkpoint(doc),undo 会回到上一个保存点的副本,其他任何行都会被输入。每条命令执行后,代码都会打印文本和已保存的文本,因此继续输入时,保存点绝不能改变。
你的代码写在 Document.R 中。main.R 包含提供的输入/输出代码,不能编辑。
亲手试试
source("Document.R")
# 提供的输入输出代码:保持原样
input <- suppressWarnings(readLines(file("stdin")))
doc <- Document$new(text = "")
saved <- checkpoint(doc)
for (cmd in input) {
if (cmd == "save") {
saved <- checkpoint(doc)
} else if (cmd == "undo") {
doc <- checkpoint(saved)
} else {
doc$type(cmd)
}
cat(cmd, " -> [", doc$text, "] saved [", saved$text, "]\n", sep = "")
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: R 在线编译器