<<-
è più utile insieme alle chiusure per mantenere lo stato. Ecco una sezione di un mio recente articolo:
Una chiusura è una funzione scritta da un'altra funzione. Le chiusure sono così chiamate perché racchiudono l'ambiente della funzione genitore e possono accedere a tutte le variabili e parametri in quella funzione. Questo è utile perché ci consente di avere due livelli di parametri. Un livello di parametri (il genitore) controlla il funzionamento della funzione. L'altro livello (il bambino) fa il lavoro. L'esempio seguente mostra come utilizzare questa idea per generare una famiglia di funzioni di potenza. La funzione genitore ( power
) crea funzioni figlio ( square
e cube
) che svolgono effettivamente il duro lavoro.
power <- function(exponent) {
function(x) x ^ exponent
}
square <- power(2)
square(2) # -> [1] 4
square(4) # -> [1] 16
cube <- power(3)
cube(2) # -> [1] 8
cube(4) # -> [1] 64
La capacità di gestire le variabili a due livelli consente anche di mantenere lo stato attraverso le invocazioni di funzioni consentendo a una funzione di modificare le variabili nell'ambiente del suo genitore. La chiave per la gestione delle variabili a diversi livelli è l'operatore di assegnazione della doppia freccia <<-
. A differenza della solita assegnazione di una freccia singola (<-
) che funziona sempre al livello corrente, l'operatore a doppia freccia può modificare le variabili nei livelli padre.
Ciò consente di mantenere un contatore che registra quante volte è stata chiamata una funzione, come mostra l'esempio seguente. Ogni volta che new_counter
viene eseguito, crea un ambiente, inizializza il contatore i
in questo ambiente e quindi crea una nuova funzione.
new_counter <- function() {
i <- 0
function() {
# do something useful, then ...
i <<- i + 1
i
}
}
La nuova funzione è una chiusura e il suo ambiente è l'ambiente di chiusura. Quando vengono chiuse counter_one
e chiuse counter_two
, ognuna modifica il contatore nel suo ambiente di chiusura e quindi restituisce il conteggio corrente.
counter_one <- new_counter()
counter_two <- new_counter()
counter_one() # -> [1] 1
counter_one() # -> [1] 2
counter_two() # -> [1] 1