方法和字段更新
本课属于 Coddy R 学习旅程的“面向对象编程”单元。第 38 课,共 57 课。
方法写在 setRefClass() 的 methods 列表中。在方法内部,字段可以直接使用名称访问,<<- 会修改字段。在 Coddy 运行器中,定义带有方法的类时,R 会输出一条关于可选 codetools 包的提示,因此要用 suppressMessages() 包裹定义:
Counter <- suppressMessages(setRefClass("Counter",
fields = list(count = "numeric"),
methods = list(
add = function(n = 1) {
count <<- count + n
},
report = function() paste("count is", count)
)
))
c1 <- Counter$new(count = 0)
c1$add()
c1$add(5)
print(c1$report())输出:
[1] "count is 6".self 是调用该方法的对象。以 invisible(.self) 结尾的方法会返回该对象,因此可以链式调用:
Counter <- suppressMessages(setRefClass("Counter",
fields = list(count = "numeric"),
methods = list(
add = function(n = 1) {
count <<- count + n
invisible(.self)
}
)
))
c1 <- Counter$new(count = 0)
c1$add(2)$add(3)$add(4)
print(c1$count)输出:
[1] 9initialize 方法会在 $new() 中运行。initFields(...) 会设置传入的字段,之后该方法可以为其他字段设置默认值。方法可以通过名称互相调用:
Account <- suppressMessages(setRefClass("Account",
fields = list(owner = "character", balance = "numeric"),
methods = list(
initialize = function(...) {
initFields(...)
if (length(balance) == 0) balance <<- 0
invisible(.self)
},
deposit = function(x) balance <<- balance + x,
deposit_twice = function(x) {
deposit(x)
deposit(x)
}
)
))
a <- Account$new(owner = "Ada")
a$deposit_twice(10)
print(a$balance)输出:
[1] 20在方法内部,对字段名使用普通的 <- 会创建一个同名的局部变量,并且不改变字段。每次修改字段时都要使用 <<-:
Counter <- suppressMessages(setRefClass("Counter",
fields = list(count = "numeric"),
methods = list(
broken = function() count <- count + 1,
working = function() count <<- count + 1
)
))
c1 <- Counter$new(count = 0)
c1$broken()
c1$broken()
c1$working()
print(c1$count)输出:
[1] 1挑战
简单在 Stack.R 中完成引用类 Stack。它将元素存储在字段 items 中(类型为字符,栈顶在最后),并有四个方法:
push(x)将x添加到栈顶,并返回invisible(.self),这样就可以链式调用入栈操作;pop()移除并返回栈顶元素,栈为空时则返回nothing;peek()返回栈顶元素但不移除它,栈为空时则返回nothing;size()返回元素个数。
定义已经由 suppressMessages() 包裹。提供的代码会读取 push a b、pop 和 peek 命令,然后通过一次链式调用再推入两个元素,并输出元素个数。
请将代码写在 Stack.R 中。main.R 中包含提供的输入/输出代码,不能编辑。
亲手试试
source("Stack.R")
# 提供的输入/输出代码:保持原样
input <- suppressWarnings(readLines(file("stdin")))
s <- Stack$new()
for (cmd in input) {
if (startsWith(cmd, "push ")) {
items <- strsplit(substring(cmd, 6), " ")[[1]]
for (it in items) s$push(it)
} else if (cmd == "pop") {
cat("popped ", s$pop(), "\n", sep = "")
} else if (cmd == "peek") {
cat("top ", s$peek(), "\n", sep = "")
}
}
s$push("x")$push("y")
cat("size after two chained pushes: ", s$size(), "\n", sep = "")
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: R 在线编译器