2011-09-24 16 views
8

Mam tło Java i Python, a ostatnio uczę się R.Jak R obsługuje obiekt w wywołaniu funkcji?

Dziś odkryłem, że R wydaje się obsługiwać obiekty zupełnie inaczej niż Java i Python.

Na przykład, następujący kod:

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

Kod daje następujący wynik:

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

Ale spodziewam druga linia wyjścia za wszystko „4” od kiedy zmodyfikował wektor w funkcji sapply.

Czy oznacza to, że R tworzy kopie obiektów w wywołaniu funkcji zamiast odniesienia do obiektów?

Odpowiedz

18

x jest zdefiniowana w globalnym środowisku, a nie w swojej funkcji.

Jeśli próbujesz zmodyfikować obiekt inny niż lokalny, taki jak x w funkcji, to R tworzy kopię obiektu i modyfikuje kopię, tak aby za każdym razem, gdy uruchomisz anonimową funkcję, utworzono kopię x i jej i-tej składowej jest ustawione na 4. Gdy funkcja wychodzi z wykonanej kopii, znika na zawsze. Oryginalny x nie został zmodyfikowany.

Gdybyśmy mieli napisać x[i] <<- i lub gdybyśmy mieli napisać x[i] <- 4; assign("x", x, .GlobalEnv), to R zapisałoby to z powrotem. Innym sposobem, aby zapisać go z powrotem byłoby ustawić e, powiedzmy, do środowiska, które x jest przechowywana i zrobić to:

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

lub ewentualnie to:

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

Normalnie jeden nie pisze takich Kod w jednym R. Rather produkuje wynik jako wyjście z funkcji takich jak to:

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

(Właściwie w tym przypadku można by pisać x[] <- 4)

. Dodano:

wykorzystaniem jednego proto package może to zrobić, gdzie sposób f ustawia-ty element właściwości do 4 x.

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 

brzmieniu:.

Dodany nad inną opcją, w której wyraźnie zdać środowisko x jest przechowywany w

+0

Dzięki! Ale dlaczego nie pisać takiego kodu w R? Czy istnieje jakieś potencjalne ryzyko lub zwykła konwencja? Myślę, że normalne jest modyfikowanie obiektów globalnych w funkcji w innych językach. –

+3

W językach funkcyjnych funkcje nie mogą mieć skutków ubocznych. R nie jest tak ścisły, ale nadal jest prawdą, że funkcje R ograniczają efekty uboczne. Lepiej pracować tak, jak zamierzano, niż próbować pisać tak, jakbyś pisał w innym języku. Istnieje kilka systemów obiektowych (S3, S4, klasy referencyjne). S3 jest najczęściej używany. S4 jest znacznie bardziej złożony. Klasy referencyjne są ostatnim dodatkiem. Możesz w szczególności zapoznać się z klasami referencyjnymi. Istnieją również pakiety dostarczane przez użytkowników, które oferują różne paradygmaty: proto i R.oo (i prawdopodobnie inne). –

+0

@Spirit Plus można użyć 'parent.frame (3)' zamiast '.GlobalEnv' do przechowywania x w zamknięciu, w którym został uruchomiony sapply, co byłoby znacznie bezpieczniejsze. (Dlaczego 3? 1-anonimowa ramka funkcyjna, 2-sapply frame, 3-sapply enclosure) – mbq

7

Tak, masz rację. Sprawdź Definicja R Język: 4.3.3 Argument Evaluation

AFAIK, R nie naprawdę skopiować dane aż starasz się go modyfikować, co w następstwie Copy-on-write semantyki.

+0

Dzięki Anatoliy! Ale czy proces kopiowania zajmowałby zbyt dużo czasu i pamięci, gdyby skopiowane dane były naprawdę duże? Lub faktycznie nie kopiuje danych, ale po prostu zneutralizować efekt modulacji na końcu wywołania funkcji? –

+0

Istnieje kopia, ale "x" wewnątrz funkcji nie jest tym samym obiektem, co poza funkcją. Istnieją środowiska i masz jedno x w środowisku wywołującym i inne x w środowisku funkcji. Tylko poprzez przypisanie wyniku zmiany będą widoczne w środowisku wywołującym. –

0

Musisz przypisać wyjście sapply do obiektu, w przeciwnym razie po prostu znika. (Właściwie można go odzyskać, gdyż również zostanie przypisany do .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

Przepraszamy, ale to nie odpowiada na pytanie i jest po prostu mylące. – mbq

+0

Teraz jestem tym zdezorientowanym. OP stworzył wektor czterech, a potem nic z nim nie zrobił. Jeśli chciał zmienić "x", musiał użyć operacji przypisania. Myślałem, że dokładnie odpowiedziałem na pytanie. –

+0

Sugerujesz, że 'v <-sapply (..., function (...) {... v ...})' konstruuje w jakiś sposób eksportowanie 'v' ze środowiska funkcji do macierzystego. Nie wspomniałem, że pytanie dotyczyło bezpośrednio tego, czy R wykonuje wywołanie po kodzie lub wywołanie referencyjne. – mbq

3

x że jest wewnątrz funkcji anonimowej jest niex w środowisko globalne (twoja przestrzeń robocza). Jest to kopia x, lokalna dla anonimowej funkcji. Nie jest tak łatwo powiedzieć, że R kopiuje obiekty w wywołaniach funkcji; R będzie dążyć do tego, aby nie kopiować, jeśli potrafi, chociaż po modyfikacji czegoś R musi skopiować obiekt.

Jak @DWin zwraca uwagę, to skopiowana wersja x który został zmodyfikowany jest zwrócony przez wywołanie sapply() Twój twierdził wyjście nie jest to, co mam:

> 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 

Oczywiste jest, że kod nie prawie co myślałeś, że to zrobi. Problem polega na tym, że dane wyjściowe z sapply() nie zostały przypisane do obiektu, a zatem są drukowane, a tym samym odrzucane.

Powód, dla którego kod działa, wynika również z reguł określania zakresu R. Naprawdę powinieneś przekazać funkcję jako argumenty do obiektów, których funkcja potrzebuje. Jeśli jednak R może: t znaleźć obiekt lokalny dla funkcji, przeszuka środowisko nadrzędne dla obiektu pasującego do nazwy, a następnie, jeśli to właściwe, elementu nadrzędnego tego środowiska, ostatecznie trafiając do środowiska globalnego, do przestrzeni roboczej. Twój kod działa, ponieważ w końcu znalazł x do pracy, ale został natychmiast skopiowany, kopia ta została zwrócona na końcu wywołania sapply().

To kopiowanie zajmuje dużo czasu i pamięci w wielu przypadkach. Jest to jeden z powodów, dla których ludzie myślą, że pętle for są wolne w R; nie przypisują pamięci dla obiektu przed wypełnieniem go pętlą. Jeśli nie przydzielisz pamięci, R musi zmodyfikować/skopiować obiekt, aby dodać następny wynik pętli.

Ponownie jednak, że nie zawsze jest to takie proste, wszędzie w badania, na przykład ze środowiskami, gdzie kopią środowisku tak naprawdę odnosi się do wersji oryginalnej:

> 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 

Jeśli rozumiesz te rodzaje rzeczy, czytając podręcznik R Language Definition, który zawiera wiele szczegółów tego, co dzieje się pod maską w R.

+0

Oops - Napisałem to kilka godzin temu (rano czasu w Wielkiej Brytanii), ale musiałem zostać odsunięte na boczny tor i nie kliknąłem przycisku przesyłania. –

0

Jeśli chcesz zmienić obiekt "globalny" z poziomu funkcji, możesz użyć przypisania innego niż lokalne.

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 

Choć w tym konkretnym przypadku można po prostu mieć go bardziej zwarty jak x[]<-4

To, nawiasem mówiąc, jedna z miłych cech R - zamiast sapply(1:10,function(i) x[i] <<- 4 lub for(i in 1:10) x[i]<-4 (for nie jest funkcja, więc nie potrzebujesz tutaj <<-) możesz po prostu napisać x[]<-4 :)

Powiązane problemy