Escritura de funciones robustas con stop() y warning()
Genere condiciones personalizadas desde sus propias funciones.
Escritura de funciones robustas con stop() y warning() es una lección gratuita de R Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de R Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de R Academy incluye 4 lecciones en total.
Por qué es importante validar las entradas
Las funciones defensivas comprueban sus entradas y fallan explícitamente con errores informativos, en lugar de producir resultados incorrectos de forma silenciosa. Una función que detecta las entradas no válidas en el límite es mucho más fácil de depurar que una que propaga datos defectuosos por toda una canalización.
# 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'))Mensajes de error informativos
Los errores genéricos, como stop('bad input'), dificultan la depuración. Use paste0() o sprintf() dentro de stop() para incluir en el mensaje el valor real y el tipo esperado. Esto ahorra tiempo al rastrear errores en canalizaciones.
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'))Validar la longitud y el rango
Validaciones habituales: comprobar que un vector no esté vacío (length(x) == 0), que un escalar esté dentro de un rango o que un marco de datos tenga las columnas esperadas. Cada comprobación debe tener su propio mensaje de error adaptado.
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'))Usar warning() para problemas no críticos
Use warning() cuando la función todavía pueda devolver un resultado razonable, pero quien la llama deba saber que ocurrió algo inusual, como convertir un valor, imputar un valor predeterminado o detectar una entrada límite.
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 en stop()
De forma predeterminada, stop() antepone Error in funcname(...): al mensaje. Pase call. = FALSE para suprimir este prefijo cuando el contexto de llamada sea evidente o cuando la función sea una utilidad destinada al usuario.
# 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'))Clases de condición personalizadas para stop()
Cree una clase de error personalizada mediante structure() para que quien llame a la función pueda capturar solo los errores de su función, sin capturar todos los errores. Esta es la forma profesional de escribir errores a nivel de paquete.
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'))Comprobar las columnas de un marco de datos
Cuando una función espera un marco de datos, valide que existan las columnas necesarias antes de usarlas. Use %in% para comprobar los nombres de las columnas y setdiff() para indicar qué columnas faltan.
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() para aserciones concisas
stopifnot(condition1, condition2, ...) lanza un error si alguna condición es FALSE. Es una forma concisa de afirmar las precondiciones al principio de una función sin escribir bloques separados de 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() con nombres para mejorar los mensajes
En R 4.0+, las expresiones con nombre en stopifnot() sustituyen el mensaje generado automáticamente por su descripción personalizada. Use el texto de la expresión como nombre para producir fallos informativos.
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'))Combinar la validación y la lógica de negocio
Una función bien estructurada separa la validación de la lógica. Valide al principio y, después, realice la operación. Esto facilita la lectura de la función y garantiza que los errores se detecten antes de comenzar cualquier cálculo.
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 dentro de funciones de utilidad
A veces una función de utilidad debe gestionar los errores internamente y devolver un valor alternativo en lugar de propagarlos. Envuelva la lógica principal en tryCatch() y devuelva un valor centinela acordado, como NA o NULL, en caso de fallo.
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')Comprobación rápida
¿Qué hace call. = FALSE cuando se pasa a stop()?
Funciones resistentes: conclusiones clave
Conclusiones clave para escribir funciones resistentes:
- Valide las entradas al principio y falle explícitamente con mensajes informativos de
stop() - Incluya el valor real en los mensajes de error:
paste0('Expected numeric, got: ', class(x)) - Use
warning()para problemas no críticos en los que el cálculo pueda continuar call. = FALSEsuprime el prefijo de la función que realiza la llamada en los mensajes de error- Use
stopifnot()para aserciones concisas y su forma con nombres para mensajes personalizados (R 4.0+) - Las clases de condición personalizadas permiten que quien llama capture errores de forma selectiva
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'))Preguntas frecuentes
¿La lección «Escritura de funciones robustas con stop() y warning()» es gratis?
Sí — el texto completo de «Escritura de funciones robustas con stop() y warning()» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de R Academy, actualiza a CoddyKit PRO. El curso de R Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Escritura de funciones robustas con stop() y warning()»?
Genere condiciones personalizadas desde sus propias funciones. Practicas R Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar R Academy?
No se requiere experiencia previa. R Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.
¿Cuánto tiempo toma la lección «Escritura de funciones robustas con stop() y warning()»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de R Academy?
Sí. Cada lección de R Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Errores, advertencias y mensajes en R
- tryCatch() para recuperar errores
- withCallingHandlers() y reinicios
- Escritura de funciones robustas con stop() y warning()