Menu
CoddyTech

验证器与辅助函数

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

构造函数会检查类型;验证器会检查值。按照惯例,validate_money() 接收一个对象,对每条不满足的规则调用 stop() 并给出清晰的消息,一切正常时则返回该对象。call. = FALSE 可以让函数调用不出现在消息中:

new_money <- function(amount = double(), currency = character()) {
  stopifnot(is.double(amount), is.character(currency))
  structure(list(amount = amount, currency = currency), class = "money")
}
validate_money <- function(x) {
  if (x$amount < 0) stop("amount must not be negative", call. = FALSE)
  if (nchar(x$currency) != 3) stop("currency must have 3 letters", call. = FALSE)
  x
}
m <- validate_money(new_money(-5, "EUR"))

输出:

Error: amount must not be negative
Execution halted

辅助函数是供其他代码调用的函数。它以类的名称命名,会将输入转换为正确的类型,填入默认值,并将结果传递给构造函数和验证函数:

new_money <- function(amount = double(), currency = character()) {
  stopifnot(is.double(amount), is.character(currency))
  structure(list(amount = amount, currency = currency), class = "money")
}
validate_money <- function(x) {
  if (x$amount < 0) stop("amount must not be negative", call. = FALSE)
  x
}
money <- function(amount, currency = "EUR") {
  validate_money(new_money(as.double(amount), toupper(currency)))
}
m <- money("12", "usd")
cat(m$amount, m$currency)

输出:

12 USD

读取用户输入的代码会用 tryCatch() 捕获验证函数的错误,并显示传递给 stop() 的文本 conditionMessage(e):

validate_age <- function(x) {
  if (x$years < 0) stop("age must not be negative", call. = FALSE)
  if (x$years > 150) stop("age is too large", call. = FALSE)
  x
}
age <- function(years) validate_age(structure(list(years = as.double(years)), class = "age"))
for (input in c("30", "-2", "200")) {
  result <- tryCatch(paste("ok", age(input)$years), error = function(e) conditionMessage(e))
  cat(result, "\n")
}

输出:

ok 30 
age must not be negative 
age is too large 

这三个函数各司其职。new_money() 严格而快速,适用于类型已经正确的代码。validate_money() 负责处理值的规则,这些规则可能很慢,也可能很多。money() 用起来更方便,程序的其他部分都会使用它:

new_money()       # 只检查类型;用 structure() 构建对象
validate_money()  # 检查值是否符合规则;不符合时用 stop() 输出消息,否则返回 x
money()           # 转换输入,然后调用 validate_money(new_money(...))
challenge icon

挑战

简单

new_score(points, max) 在 Score.R 中构建一个 score 对象。请在同一个文件中完成其他函数:

  • validate_score(x) 按顺序检查条件,否则分别以 points must not be negative、max must be positive 或 points must not exceed max 终止,并返回 x。
  • score(points, max = 100) 使用 as.double() 转换两个参数,并返回 validate_score(new_score(...))。
  • format.score(x, ...) 返回 45/50 (90%),百分比四舍五入为整数。

在每个 stop() 中使用 call. = FALSE。提供的代码会读取 45 50,打印格式化后的分数,或者打印 error: 和错误消息。

请将代码写在 Score.R 中。main.R 包含提供的输入/输出代码,不能编辑。

亲手试试

source("Score.R")

# 提供的输入和输出代码:保持原样。
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  p <- strsplit(line, " ")[[1]]
  s <- tryCatch(score(p[1], p[2]), error = function(e) conditionMessage(e))
  if (is.character(s)) cat("error: ", s, "\n", sep = "") else cat(format(s), "\n", sep = "")
}
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: R 在线编译器