Валидаторы и вспомогательные функции
Часть раздела Объектно-ориентированное программирование путешествия по 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(...))Задание
Легко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 = "")
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы S3
Работа с файламиСписки с классомФункции-конструкторыМетоды print и formatВалидаторы и вспомогательные функцииПовторение — температуры4Инкапсуляция в R
Замыкания как объектыОкружения как объектыФункции доступаЗащита состоянияПовторение — парковочный счётчик7S4 Наследование
Расширение с помощью containscallNextMethodВиртуальные классыПроверка типов с помощью is()Повторение — смотритель зоопарка10Шаблоны проектирования, часть 2
Шаблон «Декоратор»Шаблон «Строитель»Шаблон «Состояние»Шаблон «Итератор»2Обобщённые функции и методы S3
Обобщённые функции и UseMethodМетоды по умолчаниюМетоды для базовых обобщённых функцийОсновы NextMethodПовторение — площади фигур5Классы S4
Определение классов S4Слоты и доступПроверка корректностиМетоды showПовторение — каталог товаров8Ссылочные классы
Определение ссылочных классовМетоды и обновление полейСемантика копированияНаследование и callSuperПовторение — очередь задач11Проект: Управление библиотекой
Книги и читателиВыдача книг3Наследование S3
Векторы классовПорядок поиска методовРасширение с помощью NextMethodПроверка с помощью inherits()Повторение — расчёт зарплаты сотрудниковПотренируйтесь самостоятельно: Онлайн-компилятор R