Il x
che si trova all'interno della funzione anonima non è il x
nel ambiente globale (il tuo spazio di lavoro). È una copia di x
, locale per la funzione anonima. Non è così semplice dire che R copia oggetti nelle chiamate di funzione; R cercherà di non copiare se può, anche se una volta modificato qualcosa R deve copiare l'oggetto.
Come @DWin sottolinea, questa versione copiata di x
che è stato modificato è restituito dalla chiamata sapply()
, l'output dichiarato non è quello che ottengo:
> x <- c(1:10)
> print(x)
[1] 1 2 3 4 5 6 7 8 9 10
> sapply(1:10,function(i){
+ x[i] = 4
+ })
[1] 4 4 4 4 4 4 4 4 4 4
> print(x)
[1] 1 2 3 4 5 6 7 8 9 10
Chiaramente, il codice ha fatto quasi cosa hai pensato che sarebbe. Il problema è che l'output di sapply()
non è stato assegnato a un oggetto e quindi viene stampato e quindi eliminato.
Il motivo per cui si codifica funziona anche è dovuto alle regole di scoping di R. È davvero necessario passare a una funzione come argomenti tutti gli oggetti di cui la funzione ha bisogno. Tuttavia, se R può; trovare un oggetto locale alla funzione che cercherà nell'ambiente padre per un oggetto che corrisponde al nome, e quindi il genitore di quell'ambiente se appropriato, eventualmente colpendo l'ambiente globale, lo spazio di lavoro. Quindi il tuo codice funziona perché alla fine ha trovato un x
con cui lavorare, ma è stato immediatamente copiato, quella copia restituita alla fine della chiamata sapply()
.
Questa copia richiede tempo e memoria in molti casi. Questo è uno dei motivi per cui la gente pensa che i cicli for
siano lenti in R; non allocano spazio per un oggetto prima di riempirlo con un ciclo. Se non si assegna memoria, R deve modificare/copiare l'oggetto per aggiungere il successivo risultato del ciclo.
Anche in questo caso, però, non è sempre così semplice, ovunque in R, ad esempio con gli ambienti, in cui una copia di un ambiente davvero solo si riferisce alla versione originale:
> a <- new.env()
> a
<environment: 0x1af2ee0>
> b <- 4
> assign("b", b, env = a)
> a$b
[1] 4
> c <- a ## copy the environment to `c`
> assign("b", 7, env = c) ## assign something to `b` in env `c`
> c$b ## as expected
[1] 7
> a$b ## also changed `b` in `a` as `a` and `c` are actually the same thing
[1] 7
Se si capisce questo tipo di cose, leggendo il manuale R Language Definition che copre molti dei dettagli di ciò che accade sotto il cofano in R.
Grazie! Ma perché non si scrive tale codice in R? C'è qualche rischio potenziale o solo una convenzione? Penso che sia abbastanza normale modificare oggetti globali in una funzione in altre lingue. –
Nelle lingue dei linguaggi funzionali le funzioni non possono avere effetti collaterali. R non è così rigoroso, ma è ancora vero che le funzioni R limitano gli effetti collaterali. È meglio lavorare nel modo in cui doveva essere usato piuttosto che cercare di scrivere come se si stesse scrivendo in un'altra lingua. Esistono diversi sistemi di oggetti (S3, S4, Classi di riferimento). S3 è il più comunemente usato. S4 è molto più complesso. Le classi di riferimento sono una aggiunta recente. Potresti voler esplorare le classi di riferimento, in particolare. Ci sono anche alcuni pacchetti di utenti che offrono diversi paradigmi: proto e R.oo (e forse altri). –
@Spirit Plus è possibile utilizzare 'parent.frame (3)' invece di '.GlobalEnv' per memorizzare x in una chiusura in cui è stato eseguito saply, che sarebbe molto più sicuro. (Perché 3? 1 frame anonimo, struttura 2-sapply, enclosure 3-sapply) – mbq