0Pricing
R Academy · Урок

withCallingHandlers() и перезапуски

Используйте withCallingHandlers() для обработки условий за пределами локальной области.

«withCallingHandlers() и перезапуски» — бесплатный урок R Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения R Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс R Academy содержит 4 уроков всего.

tryCatch и withCallingHandlers

Ключевое различие: tryCatch() создаёт нелокальный выход — после перехвата условия управление покидает выражение и не возвращается в него. withCallingHandlers() сохраняет стек вызовов и может продолжить выполнение после обработки.

# tryCatch: execution does NOT continue after warning
tryCatch({
  warning('first')
  cat('This line is never reached\n')
}, warning = function(w) cat('tryCatch caught:', w$message, '\n'))

withCallingHandlers сохраняет выполнение

При использовании withCallingHandlers() обработчик выполняется, но после выдачи условия выполнение продолжается (если сам обработчик не выдаёт ошибку и не вызывает механизм восстановления). Стек вызовов не раскручивается.

withCallingHandlers({
  warning('first warning')
  cat('Execution continues here\n')
  warning('second warning')
  cat('And here too\n')
}, warning = function(w) {
  cat('Handler saw:', w$message, '\n')
  invokeRestart('muffleWarning')
})

Механизм восстановления muffleWarning

Вызов invokeRestart('muffleWarning') внутри обработчика предупреждения не даёт стандартному обработчику R вывести предупреждение. Без этого после выполнения Вашего обработчика R всё равно вывел бы предупреждение с помощью стандартного механизма.

# Without muffleWarning: warning still shows after handler
withCallingHandlers({
  warning('test')
}, warning = function(w) {
  cat('Handler ran\n')
  # no muffleWarning: default handler still fires
})

Механизм восстановления muffleMessage

Аналогично, invokeRestart('muffleMessage') внутри обработчика сообщения подавляет сообщение после того, как Ваш обработчик его обработает. Именно этот механизм лежит в основе suppressMessages().

withCallingHandlers({
  message('Progress: 50%')
  message('Progress: 100%')
}, message = function(m) {
  cat('[LOG]', trimws(m$message), '\n')
  invokeRestart('muffleMessage')
})

Запись предупреждений без остановки выполнения

Практическое применение withCallingHandlers(): собирайте предупреждения в вектор журнала, позволяя коду завершить работу. tryCatch() остановился бы на первом предупреждении, а withCallingHandlers() соберёт их все.

warnings_log <- character(0)
result <- withCallingHandlers({
  x <- log(-1)
  y <- sqrt(-4)
  c(x, y)
}, warning = function(w) {
  warnings_log <<- c(warnings_log, w$message)
  invokeRestart('muffleWarning')
})
cat('Warnings logged:', length(warnings_log), '\n')
cat(warnings_log, sep = '\n')

Концепция invokeRestart()

Механизмы восстановления — это заранее определённые стратегии восстановления, которые код, выдающий условие, может предложить. invokeRestart(name) выбирает одну из этих стратегий. Встроенные механизмы включают 'muffleWarning' и 'muffleMessage'; собственные механизмы можно определить с помощью withRestarts().

# withRestarts() offers named recovery options
divide_safe <- function(x, y) {
  withRestarts(
    {
      if (y == 0) stop('division by zero')
      x / y
    },
    use_zero = function() 0,
    use_na   = function() NA
  )
}
# A handler can pick a restart
withCallingHandlers(
  divide_safe(10, 0),
  error = function(e) invokeRestart('use_zero')
)

Сочетание withCallingHandlers и tryCatch

Эти два средства можно вкладывать друг в друга: используйте снаружи withCallingHandlers(), чтобы записывать условия в журнал или подсчитывать их, а внутри — tryCatch(), чтобы возвращать резервное значение. Внешний обработчик первым видит условие.

n_warnings <- 0L
result <- withCallingHandlers(
  tryCatch(log(-1), warning = function(w) -999),
  warning = function(w) {
    n_warnings <<- n_warnings + 1L
    cat('Logged warning #', n_warnings, '\n')
  }
)
cat('Result:', result, '\n')

Пользовательские классы условий

Пользовательские классы условий можно создать, вызвав structure() для списка с классом c('myError', 'error', 'condition'). Обработчики сопоставляются по классу, поэтому пользовательские классы позволяют перехватывать определённые типы ошибок, не перехватывая все ошибки.

my_error <- function(msg, data = NULL) {
  structure(
    class = c('my_error', 'error', 'condition'),
    list(message = msg, data = data)
  )
}
tryCatch(
  stop(my_error('custom error', data = 42)),
  my_error = function(e) cat('Custom handler, data=', e$data, '\n'),
  error    = function(e) cat('Generic handler\n')
)

Когда использовать withCallingHandlers

Используйте withCallingHandlers(), когда нужно наблюдать за условиями или записывать их в журнал, не останавливая выполнение. Используйте tryCatch(), когда нужно восстановиться после условия и вернуть альтернативное значение. Эти средства решают разные задачи.

# withCallingHandlers: observe all, continue
withCallingHandlers(
  for (x in c(4, -1, 9, -4)) cat(suppressWarnings(sqrt(x)), ''),
  warning = function(w) cat('[warn]', '')
)
cat('\n')

Цепочка выдачи условий

Когда выдаётся условие, R поднимается по стеку вызовов в поисках обработчиков. Обработчики withCallingHandlers() выполняются в динамическом контексте (стек вызовов сохраняется). Обработчики tryCatch() выполняются после раскрутки стека — исходный контекст уже недоступен.

# Demonstrate: withCallingHandlers handler can see full stack
f <- function() {
  withCallingHandlers(
    g(),
    warning = function(w) {
      cat('Caught in f, stack length:', sys.nframe(), '\n')
      invokeRestart('muffleWarning')
    }
  )
}
g <- function() warning('from g')
f()

Реализация suppressWarnings()

suppressWarnings(expr) по сути является withCallingHandlers(expr, warning = function(w) invokeRestart('muffleWarning')). Понимание этого позволяет создавать собственные варианты, подавляющие только определённые типы предупреждений.

# Suppress only NaN warnings, not others
suppress_nan_warn <- function(expr) {
  withCallingHandlers(expr, warning = function(w) {
    if (grepl('NaN', conditionMessage(w)))
      invokeRestart('muffleWarning')
  })
}
suppress_nan_warn(log(-1))   # NaN warning suppressed
tryCatch(
  suppress_nan_warn(warning('other issue')),
  warning = function(w) cat('Other warning kept:', w$message, '\n')
)

Быстрая проверка

В чём заключается главное различие между withCallingHandlers() и tryCatch()?

Механизмы восстановления: главное

Главное о withCallingHandlers и механизмах восстановления:

  • withCallingHandlers() = локальный обработчик, стек вызовов сохраняется, выполнение может продолжаться
  • tryCatch() = нелокальный выход, стек раскручивается, можно вернуть альтернативное значение
  • invokeRestart('muffleWarning') подавляет стандартный вывод предупреждения
  • invokeRestart('muffleMessage') подавляет стандартный вывод сообщения
  • Используйте withCallingHandlers(), чтобы собирать или записывать в журнал все условия, не останавливая выполнение
  • Пользовательские классы условий позволяют выборочно перехватывать определённые типы ошибок
log_vec <- character(0)
withCallingHandlers({
  message('start')
  warning('low memory')
  message('end')
}, message = function(m) {
  log_vec <<- c(log_vec, paste('MSG:', trimws(m$message)))
  invokeRestart('muffleMessage')
}, warning = function(w) {
  log_vec <<- c(log_vec, paste('WARN:', w$message))
  invokeRestart('muffleWarning')
})
cat(log_vec, sep = '\n')

Часто задаваемые вопросы

Урок «withCallingHandlers() и перезапуски» бесплатный?

Да — полный текст урока «withCallingHandlers() и перезапуски» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс R Academy, подпишись на CoddyKit PRO. Курс R Academy содержит 4 уроков всего.

Чему я научусь в уроке «withCallingHandlers() и перезапуски»?

Используйте withCallingHandlers() для обработки условий за пределами локальной области. Ты практикуешь R Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать R Academy?

Предыдущий опыт не требуется. R Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «withCallingHandlers() и перезапуски»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке R Academy?

Да. Каждый урок R Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

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

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