2011-09-24 16 views
8

Ho uno sfondo di Java e Python e sto imparando R di recente.In che modo R gestisce l'oggetto nella chiamata di funzione?

Oggi ho scoperto che R sembra gestire gli oggetti in modo molto diverso da Java e Python.

Ad esempio, il seguente codice:

x <- c(1:10) 
print(x) 
sapply(1:10,function(i){ 
      x[i] = 4 
     }) 
print(x) 

Il codice dà il seguente risultato:

[1] 1 2 3 4 5 6 7 8 9 10 
[1] 1 2 3 4 5 6 7 8 9 10 

ma mi aspetto che la seconda linea di uscita sia all '4' da quando ho modificato il vettore nella funzione sapply.

Questo significa che R crea copie di oggetti nella chiamata di funzione anziché fare riferimento agli oggetti?

risposta

18

x è definito nel contesto globale, non nella vostra funzione.

Se si tenta di modificare un oggetto non locale, come x in una funzione, allora R fa una copia dell'oggetto e modifica la copia in modo che ogni volta che si esegue la funzione anonima una copia di x è fatto e la sua componente-esima è impostato su 4. Quando la funzione esce, la copia creata scompare per sempre. L'originale x non è stato modificato.

Se dovessimo scrivere x[i] <<- i o se dovessimo scrivere x[i] <- 4; assign("x", x, .GlobalEnv) allora R lo scriverebbe di nuovo. Un altro modo di scrivere di nuovo sarebbe impostare e, per esempio, l'ambiente che x è memorizzato in e fare questo:

e <- environment() 
sapply(1:10, function(i) e$x[i] <- 4) 

o eventualmente questo:

sapply(1:10, function(i, e) e$x[i] <- 4, e = environment()) 

Normalmente uno non scrive tale codice in R. Piuttosto si produce il risultato come l'uscita della funzione come questa:

x <- sapply(1:10, function(i) 4) 

(in realtà in questo caso si potrebbe scrivere x[] <- 4)

aggiunto:.

Uso del proto package si potrebbe fare questo metodo where f imposta il costituente la x struttura a 4.

library(proto) 

p <- proto(x = 1:10, f = function(., i) .$x[i] <- 4) 

for(i in seq_along(p$x)) p$f(i) 
p$x 

aggiunto:.

Aggiunto sopra un'altra opzione in cui si passa in modo esplicito l'ambiente che x è memorizzato in

+0

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. –

+3

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). –

+0

@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

7

Sì, hai ragione. Verifica la definizione del linguaggio R: 4.3.3 Argument Evaluation

AFAIK, R in realtà non copia i dati finché non si sta tentando di modificarli, seguendo quindi la semantica Copy-on-write.

+0

Grazie Anatoliy! Ma il processo di copia richiederebbe troppo tempo e poca memoria se i dati copiati sono veramente grandi? O in realtà non copia i dati, ma neutralizza solo l'effetto di modificazione alla fine della chiamata di funzione? –

+0

C'è una copia, ma la "x" all'interno della funzione non è lo stesso oggetto di quella esterna alla funzione. Ci sono ambienti e si ha una x nell'ambiente di chiamata e una x differente nell'ambiente di funzione. Solo con l'assegnazione del risultato le modifiche saranno visibili nell'ambiente di chiamata. –

0

È necessario assegnare l'output di sapply a un oggetto, altrimenti scompare. (In realtà è possibile recuperarlo dal momento che anche viene assegnato a .Last.value)

x <- c(1:10) 
print(x) 
[1] 1 2 3 4 5 6 7 8 9 10 
x <- sapply(1:10,function(i){ 
      x[i] = 4 
     }) 
print(x) 
[1] 4 4 4 4 4 4 4 4 4 4 
+0

Siamo spiacenti, ma questo non risponde alla domanda ed è solo fonte di confusione. – mbq

+0

Ora sono confuso. L'OP ha creato un vettore di 4 e quindi non ha fatto nulla con esso. Se voleva "x" cambiare, doveva usare un'operazione di assegnazione. Pensavo di aver risposto esattamente alla domanda. –

+0

Sembra suggerire che 'v <-sapply (..., function (...) {... v ...})' costruisca in qualche modo esporta 'v' dall'ambiente delle funzioni a quello padre. Per non parlare della domanda era abbastanza diretto sul fatto che R faccia call-by-copy o call-by-reference. – mbq

3

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.

+0

Oops - Ho scritto questa ora fa (ora del mattino del Regno Unito) ma devo essere sviato e non fare clic sul pulsante di invio. –

0

Se si desidera modificare un oggetto "globale" all'interno di una funzione, è possibile utilizzare l'assegnazione non locale.

x <- c(1:10) 
# [1] 1 2 3 4 5 6 7 8 9 10 
print(x) 
sapply(1:10,function(i){ 
      x[i] <<- 4 
     }) 
print(x) 
# [1] 4 4 4 4 4 4 4 4 4 4 

Anche se in questo caso particolare, si può solo avere più compatto come x[]<-4

Questo è, tra l'altro, una delle caratteristiche interessanti di R - invece di sapply(1:10,function(i) x[i] <<- 4 o for(i in 1:10) x[i]<-4 (for non è una funzione, quindi non è necessario <<- qui) si può solo scrivere x[]<-4 :)

Problemi correlati