Надёжные функции с stop() и warning()
Генерируйте пользовательские условия внутри собственных функций.
«Надёжные функции с stop() и warning()» — бесплатный урок R Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения R Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс R Academy содержит 4 уроков всего.
Почему важна проверка входных данных
Защитные функции проверяют входные данные и явно завершаются с информативными ошибками, вместо того чтобы незаметно выдавать неверные результаты. Функцию, которая отбраковывает некорректные входные данные на границе, намного проще отлаживать, чем функцию, которая передаёт ошибочные данные дальше по конвейеру.
# Without validation: wrong result, no error
bad_mean <- function(x) sum(x) / length(x)
bad_mean('hello') # no error, just NA
# With validation: clear error
good_mean <- function(x) {
if (!is.numeric(x)) stop('x must be numeric')
sum(x) / length(x)
}
tryCatch(good_mean('hello'), error = function(e) cat(e$message, '\n'))Информативные сообщения об ошибках
Общие ошибки вроде stop('bad input') затрудняют отладку. Используйте paste0() или sprintf() внутри stop(), чтобы включить в сообщение фактическое значение и ожидаемый тип. Это экономит время при отслеживании ошибок в конвейерах.
check_numeric <- function(x, arg_name = 'x') {
if (!is.numeric(x)) {
stop(sprintf(
"'%s' must be numeric, but got class '%s'",
arg_name, class(x)
))
}
}
tryCatch(check_numeric('abc', 'score'),
error = function(e) cat(e$message, '\n'))Проверка длины и диапазона
Распространённые проверки: убедиться, что вектор не пуст (length(x) == 0), что скаляр находится в допустимом диапазоне, а таблица данных содержит ожидаемые столбцы. Для каждой проверки следует подготовить собственное подходящее сообщение об ошибке.
validate_prob <- function(p) {
if (!is.numeric(p)) stop(paste0('p must be numeric, got: ', class(p)))
if (length(p) != 1) stop(paste0('p must be length 1, got: ', length(p)))
if (p < 0 || p > 1) stop(paste0('p must be in [0,1], got: ', p))
p
}
cat(validate_prob(0.7), '\n')
tryCatch(validate_prob(1.5), error = function(e) cat(e$message, '\n'))Использование warning() для некритичных проблем
Используйте warning(), когда функция всё ещё может вернуть разумный результат, но вызывающему коду нужно сообщить о необычной ситуации — например, о приведении типа, подстановке значения по умолчанию или обнаружении пограничного входного значения.
clamp <- function(x, lo, hi) {
if (any(x < lo)) warning(paste0(sum(x < lo), ' value(s) below lo, clamped'))
if (any(x > hi)) warning(paste0(sum(x > hi), ' value(s) above hi, clamped'))
pmax(lo, pmin(hi, x))
}
result <- clamp(c(2, -5, 7, 100, 4), 0, 10)
cat('Result:', result, '\n')call. = FALSE в stop()
По умолчанию stop() добавляет перед сообщением префикс Error in funcname(...):. Передайте call. = FALSE, чтобы скрыть этот префикс, если контекст вызова очевиден или функция предназначена для непосредственного использования пользователем.
# With call. = TRUE (default): shows calling function
f1 <- function(x) stop('bad input')
tryCatch(f1(1), error = function(e) cat(conditionMessage(e), '\n'))
# With call. = FALSE: cleaner message
f2 <- function(x) stop('bad input', call. = FALSE)
tryCatch(f2(1), error = function(e) cat(conditionMessage(e), '\n'))Пользовательские классы условий для stop()
Создайте пользовательский класс ошибки с помощью structure(), чтобы вызывающий код мог перехватывать только ошибки Вашей функции, не перехватывая все ошибки. Это профессиональный способ создания ошибок на уровне пакета.
value_error <- function(msg, call = sys.call(-1)) {
structure(
class = c('value_error', 'error', 'condition'),
list(message = msg, call = call)
)
}
check_positive <- function(x) {
if (x <= 0) stop(value_error(paste0('Expected positive, got: ', x)))
x
}
tryCatch(check_positive(-3),
value_error = function(e) cat('ValueError:', e$message, '\n'),
error = function(e) cat('Other error:', e$message, '\n'))Проверка столбцов таблицы данных
Если функция ожидает таблицу данных, проверьте наличие обязательных столбцов до их использования. Используйте %in% для проверки имён столбцов и setdiff(), чтобы сообщить, какие столбцы отсутствуют.
require_cols <- function(df, cols) {
missing <- setdiff(cols, names(df))
if (length(missing) > 0) {
stop(paste0('Missing columns: ', paste(missing, collapse = ', ')))
}
invisible(df)
}
df <- data.frame(x = 1:3, y = 4:6)
tryCatch(
require_cols(df, c('x', 'z', 'w')),
error = function(e) cat(e$message, '\n')
)stopifnot() для кратких утверждений
stopifnot(condition1, condition2, ...) выдаёт ошибку, если любое условие равно FALSE. Это краткий способ проверить предварительные условия в начале функции без написания отдельных блоков if (!...) stop(...).
compute_area <- function(width, height) {
stopifnot(
is.numeric(width),
is.numeric(height),
width > 0,
height > 0
)
width * height
}
cat(compute_area(5, 3), '\n')
tryCatch(compute_area(-1, 3), error = function(e) cat(e$message, '\n'))Именованный stopifnot() для улучшения сообщений
В R 4.0+ именованные выражения в stopifnot() заменяют автоматически созданное сообщение собственным описанием. Используйте текст выражения в качестве имени, чтобы получать информативные сообщения о сбоях.
validate_age <- function(age) {
stopifnot(
'age must be numeric' = is.numeric(age),
'age must be positive' = age > 0,
'age must be under 150' = age < 150
)
invisible(age)
}
tryCatch(validate_age(-5), error = function(e) cat(e$message, '\n'))
tryCatch(validate_age('x'), error = function(e) cat(e$message, '\n'))Сочетание проверки и прикладной логики
Хорошо структурированная функция отделяет проверку от логики. Сначала выполните проверку, затем операцию. Так функцию проще читать, а ошибки обнаруживаются до начала вычислений.
discount_price <- function(price, pct) {
if (!is.numeric(price) || price <= 0)
stop(paste0('price must be positive numeric, got: ', price))
if (!is.numeric(pct) || pct < 0 || pct > 100)
stop(paste0('pct must be in [0,100], got: ', pct))
if (pct > 50) warning('discount > 50% is unusual')
price * (1 - pct / 100)
}
cat(discount_price(100, 20), '\n')
cat(suppressWarnings(discount_price(100, 60)), '\n')tryCatch внутри вспомогательных функций
Иногда вспомогательная функция должна обрабатывать ошибки внутри себя и возвращать резервное значение, а не передавать ошибки дальше. Оберните основную логику в tryCatch() и при сбое возвращайте согласованное специальное значение, например NA или NULL.
safe_log <- function(x) {
tryCatch({
if (!is.numeric(x)) stop('not numeric')
if (x <= 0) stop('must be positive')
log(x)
}, error = function(e) {
warning(paste0('safe_log failed for x=', x, ': ', e$message))
NA_real_
})
}
results <- sapply(list(10, -1, 'a', 100), safe_log)
cat(results, '\n')Быстрая проверка
Что делает call. = FALSE, переданный в stop()?
Надёжные функции: главное
Главное о написании надёжных функций:
- Проверяйте входные данные в начале; при ошибке явно завершайте выполнение с информативными сообщениями
stop() - Включайте фактическое значение в сообщения об ошибках:
paste0('Expected numeric, got: ', class(x)) - Используйте
warning()для некритичных проблем, при которых вычисление может продолжаться call. = FALSEскрывает префикс вызывающей функции в сообщениях об ошибках- Используйте
stopifnot()для кратких утверждений, а именованную форму — для собственных сообщений (R 4.0+) - Пользовательские классы условий позволяют вызывающему коду выполнять выборочное перехватывание
robust_divide <- function(x, y) {
stopifnot('x must be numeric' = is.numeric(x),
'y must be numeric' = is.numeric(y))
if (y == 0) stop('Division by zero', call. = FALSE)
if (abs(y) < 1e-10) warning('y is very small; result may be inaccurate')
x / y
}
cat(robust_divide(10, 2), '\n')
tryCatch(robust_divide(10, 0), error = function(e) cat(e$message, '\n'))Изучай R с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 43
- Уроки
- 159
Часто задаваемые вопросы
Урок «Надёжные функции с stop() и warning()» бесплатный?
Да — полный текст урока «Надёжные функции с stop() и warning()» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс R Academy, подпишись на CoddyKit PRO. Курс R Academy содержит 4 уроков всего.
Чему я научусь в уроке «Надёжные функции с stop() и warning()»?
Генерируйте пользовательские условия внутри собственных функций. Ты практикуешь R Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать R Academy?
Предыдущий опыт не требуется. R Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Надёжные функции с stop() и warning()»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке R Academy?
Да. Каждый урок R Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Ошибки, предупреждения и сообщения в R
- tryCatch() для восстановления после ошибок
- withCallingHandlers() и перезапуски
- Надёжные функции с stop() и warning()