Funzioni in R
Le funzioni sono blocchi di codice riutilizzabili che svolgono un compito specifico. In R, le funzioni sono oggetti di prima classe: possono essere assegnate a variabili, passate come argomenti e restituite da altre funzioni.
Definire Funzioni con function()
Per creare una funzione in R si utilizza la parola chiave function(), specificando i parametri tra parentesi e il corpo della funzione tra parentesi graffe.
# Definizione di una funzione base
saluta <- function(nome) {
messaggio <- paste("Ciao,", nome, "! Benvenuto in R.")
return(messaggio)
}
saluta("Marco")
# [1] "Ciao, Marco ! Benvenuto in R."
Parametri e Argomenti di Default
I parametri possono avere valori predefiniti, rendendo la funzione piu flessibile.
# Funzione con argomenti di default
calcola_area <- function(base, altezza, forma = "rettangolo") {
if (forma == "rettangolo") {
area <- base * altezza
} else if (forma == "triangolo") {
area <- (base * altezza) / 2
} else {
stop("Forma non supportata")
}
return(area)
}
calcola_area(5, 3)
# [1] 15
calcola_area(5, 3, forma = "triangolo")
# [1] 7.5
return() vs Ultimo Valore
In R, una funzione restituisce automaticamente l’ultimo valore valutato. L’uso esplicito di return() non e obbligatorio, ma migliora la leggibilita.
# Con return() esplicito
somma_esplicita <- function(a, b) {
risultato <- a + b
return(risultato)
}
# Senza return(): restituisce l'ultimo valore
somma_implicita <- function(a, b) {
a + b
}
somma_esplicita(3, 7)
# [1] 10
somma_implicita(3, 7)
# [1] 10
Quando la funzione contiene logica condizionale, return() esplicito e consigliato per chiarezza:
classifica_voto <- function(voto) {
if (voto >= 28) return("Ottimo")
if (voto >= 24) return("Buono")
if (voto >= 18) return("Sufficiente")
return("Insufficiente")
}
classifica_voto(30)
# [1] "Ottimo"
classifica_voto(20)
# [1] "Sufficiente"
Funzioni Anonime
Le funzioni anonime non hanno un nome e vengono usate direttamente, spesso come argomento di altre funzioni.
# Funzione anonima usata con sapply
numeri <- 1:5
sapply(numeri, function(x) x^2 + 1)
# [1] 2 5 10 17 26
# Dalla versione R 4.1+, sintassi abbreviata con \()
sapply(numeri, \(x) x^3)
# [1] 1 8 27 64 125
Scope delle Variabili
R utilizza il lexical scoping: una funzione cerca le variabili prima nel proprio ambiente locale, poi in quello genitore.
x <- 10 # Variabile globale
funzione_esempio <- function() {
y <- 20 # Variabile locale
cat("x (globale):", x, "\n")
cat("y (locale):", y, "\n")
}
funzione_esempio()
# x (globale): 10
# y (locale): 20
# y non e accessibile fuori dalla funzione
# print(y) # Errore: oggetto 'y' non trovato
Per modificare una variabile globale dall’interno di una funzione, si usa l’operatore <<-:
contatore <- 0
incrementa <- function() {
contatore <<- contatore + 1
}
incrementa()
incrementa()
incrementa()
print(contatore)
# [1] 3
I Tre Punti (…) per Argomenti Variabili
L’argomento speciale ... (dots) permette di passare un numero variabile di argomenti a una funzione.
# Funzione che accetta un numero variabile di argomenti
media_pesata <- function(..., pesi = NULL) {
valori <- c(...)
if (is.null(pesi)) {
return(mean(valori))
}
return(sum(valori * pesi) / sum(pesi))
}
media_pesata(10, 20, 30)
# [1] 20
media_pesata(10, 20, 30, pesi = c(1, 2, 3))
# [1] 23.33333
# Passare ... ad altre funzioni
mio_grafico <- function(x, y, ...) {
plot(x, y, ...)
}
# Gli argomenti extra vengono passati a plot()
# mio_grafico(1:10, (1:10)^2, col = "red", pch = 19, main = "Quadrati")
Funzioni Ricorsive
Una funzione ricorsiva richiama se stessa. E utile per problemi che possono essere suddivisi in sotto-problemi identici.
# Fattoriale ricorsivo
fattoriale <- function(n) {
if (n <= 1) return(1)
return(n * fattoriale(n - 1))
}
fattoriale(5)
# [1] 120
fattoriale(10)
# [1] 3628800
# Fibonacci ricorsivo
fibonacci <- function(n) {
if (n <= 1) return(n)
return(fibonacci(n - 1) + fibonacci(n - 2))
}
sapply(0:10, fibonacci)
# [1] 0 1 1 2 3 5 8 13 21 34 55
La Famiglia Apply
Le funzioni della famiglia apply permettono di applicare una funzione a elementi di strutture dati senza usare cicli espliciti, rendendo il codice piu conciso e spesso piu efficiente.
sapply() - Semplifica il risultato
sapply() applica una funzione a ogni elemento di un vettore o lista e restituisce un vettore o matrice.
# Applicare una funzione a ogni elemento
numeri <- list(1:5, 6:10, 11:15)
sapply(numeri, mean)
# [1] 3 8 13
sapply(numeri, sum)
# [1] 15 40 65
lapply() - Restituisce una lista
lapply() funziona come sapply() ma restituisce sempre una lista.
parole <- c("ciao", "mondo", "r")
lapply(parole, toupper)
# [[1]]
# [1] "CIAO"
# [[2]]
# [1] "MONDO"
# [[3]]
# [1] "R"
tapply() - Applica per gruppi
tapply() applica una funzione a sottogruppi di un vettore definiti da un fattore.
# Vendite per regione
vendite <- c(100, 200, 150, 300, 250, 180)
regione <- c("Nord", "Sud", "Nord", "Sud", "Nord", "Sud")
tapply(vendite, regione, mean)
# Nord Sud
# 166.6667 226.6667
tapply(vendite, regione, sum)
# Nord Sud
# 500 680
mapply() - Versione multivariata
mapply() applica una funzione a piu vettori in parallelo, elemento per elemento.
# Applicare una funzione a piu vettori
basi <- c(3, 5, 7)
altezze <- c(4, 6, 8)
mapply(function(b, h) b * h / 2, basi, altezze)
# [1] 6 15 28
Conclusione
Le funzioni sono il cuore della programmazione in R. Permettono di organizzare il codice in blocchi riutilizzabili, migliorando leggibilita e manutenibilita. Padroneggiare i concetti di scope, argomenti variabili e la famiglia apply consente di scrivere codice R idiomatico ed efficiente, evitando cicli espliciti e sfruttando appieno la natura vettoriale del linguaggio.