环境作为对象
本课属于 Coddy R 学习旅程的“面向对象编程”单元。第 18 课,共 57 课。
环境是一组带名称的值,类似于列表,可以用 new.env() 创建。不同之处在于,环境永远不会被复制:每个引用它的变量看到的都是同一个对象,因此通过一个名称进行的更改,通过其他所有名称也都能看到:
e <- new.env()
e$count <- 1
alias <- e
alias$count <- 99
print(e$count)
l <- list(count = 1)
copy <- l
copy$count <- 99
print(l$count)输出:
[1] 99
[1] 1接收环境的函数会更改调用方的对象。它不必返回任何内容,调用方也不会赋值接收结果:
deposit <- function(acc, x) acc$balance <- acc$balance + x
acc <- new.env()
acc$balance <- 10
deposit(acc, 5)
deposit(acc, 5)
print(acc$balance)输出:
[1] 20assign()、get() 和 exists() 可以处理存储在变量中的名称,ls() 会按排序后的顺序列出环境中的名称:
e <- new.env()
for (fruit in c("pear", "apple")) assign(fruit, nchar(fruit), envir = e)
print(ls(e))
print(get("pear", envir = e))
print(exists("kiwi", envir = e))输出:
[1] "apple" "pear"
[1] 4
[1] FALSE带有类属性的环境是具有引用语义的 S3 对象:它的方法照常工作,修改它的函数会直接在原对象上进行修改:
new_wallet <- function(coins = 0) {
w <- new.env()
w$coins <- coins
class(w) <- "wallet"
w
}
print.wallet <- function(x, ...) {
cat("wallet with", x$coins, "coins\n")
invisible(x)
}
spend <- function(w, n) w$coins <- w$coins - n
w <- new_wallet(10)
spend(w, 3)
w输出:
wallet with 7 coins挑战
简单库存是一个类为 inventory 的环境,其中有一个变量 items,它是一个带名称的数值型库存向量。完成 Inventory.R:
new_inventory()创建一个环境,其中包含空的items(使用c()即可)和类。add_stock(inv, item, n)将n加到该商品的库存中。新商品的初始库存为 0。take_stock(inv, item, n)在库存充足时扣除n并返回TRUE,否则不做任何更改并返回FALSE。stock_of(inv, item)返回库存量;商品未知时返回 0。
这些函数会直接修改库存,不会返回库存对象。提供的代码会读取命令 add apple 5 和 take apple 2,打印每条 take 命令的结果,然后按字母顺序打印所有商品。
你的代码应写在 Inventory.R 中。main.R 包含提供的输入/输出代码,不能编辑。
亲手试试
source("Inventory.R")
# 提供的输入和输出代码:保持原样
input <- suppressWarnings(readLines(file("stdin")))
inv <- new_inventory()
for (line in input) {
p <- strsplit(line, " ")[[1]]
n <- as.numeric(p[3])
if (p[1] == "add") add_stock(inv, p[2], n)
if (p[1] == "take") cat("take ", p[2], ": ", if (take_stock(inv, p[2], n)) "ok" else "not enough", "\n", sep = "")
}
for (item in sort(names(inv$items))) cat(item, ": ", stock_of(inv, item), "\n", sep = "")
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: R 在线编译器