R Academy · Урок

Надёжные функции с stop() и warning()

Генерируйте пользовательские условия внутри собственных функций.

Урок 4 из 413 шагов

«Надёжные функции с 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 — локальная установка не требуется.

Все уроки этого курса

  1. Ошибки, предупреждения и сообщения в R
  2. tryCatch() для восстановления после ошибок
  3. withCallingHandlers() и перезапуски
  4. Надёжные функции с stop() и warning()
← Назад к R Academy