Menu
Coddy logo textTech

Валидаторы и вспомогательные функции

Часть раздела Объектно-ориентированное программирование путешествия по R на Coddy. Урок 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() и показывает conditionMessage(e) — текст, переданный в stop():

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() с сообщением, иначе return 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%), округляя процент до целого числа.

Используйте call. = FALSE в каждом вызове stop(). Предоставленный код считывает строки 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