Scrittura di funzioni robuste con stop() e warning()
Generi condizioni personalizzate all'interno delle Sue funzioni.
Scrittura di funzioni robuste con stop() e warning() è una lezione R Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento R Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso R Academy include 4 lezioni in totale.
Perché la convalida degli input è importante
Le funzioni difensive controllano gli input e generano errori informativi in modo esplicito, invece di produrre silenziosamente risultati errati. Una funzione che intercetta gli input non validi al confine è molto più facile da eseguire il debug rispetto a una che propaga dati errati lungo una pipeline.
# 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'))Messaggi di errore informativi
Gli errori generici, come stop('bad input'), rendono frustrante il debug. Utilizzi paste0() o sprintf() all'interno di stop() per includere nel messaggio il valore effettivo e il tipo previsto. Questo fa risparmiare tempo quando si tracciano gli errori nelle pipeline.
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'))Convalidare lunghezza e intervallo
Convalide comuni: verificare che un vettore non sia vuoto (length(x) == 0), che uno scalare rientri in un determinato intervallo o che un data frame contenga le colonne previste. Ogni controllo dovrebbe avere un messaggio di errore specifico.
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'))Utilizzare warning() per i problemi non fatali
Utilizzi warning() quando la funzione può comunque restituire un risultato sensato, ma chi la chiama deve sapere che si è verificato qualcosa di insolito, ad esempio la conversione di un valore, l'imputazione di un valore predefinito o il rilevamento di un input al limite.
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 in stop()
Per impostazione predefinita, stop() antepone al messaggio Error in funcname(...):. Passi call. = FALSE per sopprimere questo prefisso quando il contesto della chiamata è evidente o quando la funzione è un'utilità destinata agli utenti.
# 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'))Classi di condizioni personalizzate per stop()
Crei una classe di errore personalizzata utilizzando structure(), così chi chiama la funzione può intercettare solo gli errori della Sua funzione senza intercettare tutti gli errori. Questo è il modo professionale di scrivere errori a livello di pacchetto.
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'))Controllare le colonne di un data frame
Quando una funzione richiede un data frame, verifichi che le colonne necessarie esistano prima di utilizzarle. Utilizzi %in% per controllare i nomi delle colonne e setdiff() per indicare quali colonne mancano.
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() per asserzioni concise
stopifnot(condition1, condition2, ...) genera un errore se una qualsiasi condizione è FALSE. È un modo conciso per asserire i prerequisiti all'inizio di una funzione senza scrivere blocchi separati 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 nomi per messaggi migliori
In R 4.0+, le espressioni con nome in stopifnot() sostituiscono il messaggio generato automaticamente con una descrizione personalizzata. Utilizzi il testo dell'espressione come nome per produrre messaggi di errore informativi.
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'))Combinare convalida e logica applicativa
Una funzione ben strutturata separa la convalida dalla logica. Convalidi all'inizio, poi esegua l'operazione. Questo rende la funzione più facile da leggere e garantisce che gli errori vengano rilevati prima dell'inizio di qualsiasi calcolo.
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 all'interno delle funzioni di utilità
A volte una funzione di utilità dovrebbe gestire internamente gli errori e restituire un valore di riserva invece di propagarli. Racchiuda la logica principale in tryCatch() e restituisca un valore sentinella concordato, come NA o NULL, in caso di errore.
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')Verifica rapida
Che cosa fa call. = FALSE quando viene passato a stop()?
Funzioni robuste: punti chiave
Punti chiave per scrivere funzioni robuste:
- Convalidi gli input all'inizio e generi errori espliciti con messaggi informativi di
stop() - Includa il valore effettivo nei messaggi di errore:
paste0('Expected numeric, got: ', class(x)) - Utilizzi
warning()per i problemi non fatali, quando il calcolo può continuare call. = FALSEsopprime il prefisso della funzione chiamante nei messaggi di errore- Utilizzi
stopifnot()per asserzioni concise e la forma con nome per messaggi personalizzati (R 4.0+) - Le classi di condizioni personalizzate consentono a chi chiama la funzione di intercettare selettivamente gli errori
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'))Domande Frequenti
La lezione «Scrittura di funzioni robuste con stop() e warning()» è gratuita?
Sì — il testo completo di «Scrittura di funzioni robuste con stop() e warning()» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso R Academy, passa a CoddyKit PRO. Il corso R Academy include 4 lezioni in totale.
Cosa imparerò in «Scrittura di funzioni robuste con stop() e warning()»?
Generi condizioni personalizzate all'interno delle Sue funzioni. Eserciti R Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare R Academy?
Non è richiesta alcuna esperienza precedente. R Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Scrittura di funzioni robuste con stop() e warning()»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione R Academy?
Sì. Ogni lezione R Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Errori, avvisi e messaggi in R
- tryCatch() per il recupero dagli errori
- withCallingHandlers() e i restart
- Scrittura di funzioni robuste con stop() e warning()