2013-08-20 10 views
6

Piszę niektóre funkcje do wykonywania powtarzających się zadań, ale staram się zminimalizować ilość razy, kiedy ładuję dane. Zasadniczo mam jedną funkcję, która pobiera pewne informacje i tworzy fabułę. Następnie mam drugą funkcję, która przejdzie w pętlę i wypisze wiele wykresów do pliku .pdf. W obu tych funkcjach Mam następujący wiersz kodu:Zagnieżdżone środowisko wyboru funkcji

if(load.dat) load("myworkspace.RData") 

gdzie load.dat jest logiczne, a dane potrzebne są przechowywane w myworkspace.RData. Kiedy wywołuję funkcję wrappera, która wykonuje pętle i generuje wiele wykresów, nie chcę ponownie ładować obszaru roboczego w każdym wywołaniu funkcji wewnętrznej. Pomyślałem, że mógłbym załadować przestrzeń roboczą raz w funkcji opakowania, wtedy wewnętrzna funkcja mogłaby uzyskać dostęp do tych danych, ale dostałem błąd stwierdzający inaczej.

Moje zrozumienie było wtedy, gdy funkcja nie może znaleźć zmiennej w swoim środowisku lokalnym (utworzonej po wywołaniu funkcji), funkcja będzie wyglądać dla środowiska macierzystego dla zmiennej.

Założono, że środowisko macierzyste do wewnętrznego wywołania funkcji będzie zewnętrznym wywołaniem funkcji. Oczywiście nie jest to prawda:

func1 <- function(...){ 
    print(var1) 
} 

func2 <- function(...){ 
    var1 <- "hello" 
    func1(...) 
} 

> func2() 
Error in print(var1) : object 'var1' not found 

Po przeczytaniu liczne pytania, podręcznik języka i this bardzo pomocny blogu, wpadłem na następujący:

var1 <- "hello" 
save(list="var1",file="test.RData") 
rm(var1) 

func3 <- function(...){ 
    attach("test.RData") 
    func1(...) 
    detach("file:test.RData") 
} 

> func3() 
[1] "hello" 

Czy istnieje lepszy sposób to zrobić to? Dlaczego func1 nie szuka niezdefiniowanych zmiennych w lokalnym środowisku utworzonym przez func2, gdy był func2, który nazywał się func1?

Uwaga: Nie wiedziałem, jak nazwać to pytanie. Jeśli ktoś ma lepsze sugestie, zmienię go i zredaguję tę linię.

+2

Lexical scopingu oznacza, że ​​funkcja będzie szukać niezdefiniowanych symboli w jego otoczeniu dominującej, która niekoniecznie jest środowisko dzwoni. Sprawdź również: https://github.com/hadley/devtools/wiki/Environments –

+0

@ Ferdinand.kraft Dzięki za link. Przejdę przez to dziś po południu. – dayne

+0

Jeśli twoje dane mają postać ramek danych, możesz użyć pakietu 'data.table' i przekazać tabele jako argument funkcji' func1' wewnątrz 'func3'. Ten pakiet działa przez odniesienie i nie tworzy niechcianych kopii danych. –

Odpowiedz

7

Aby zilustrować leksykalny scopingu, należy rozważyć następujące:

Najpierw stwórzmy środowisko piaskownicy, tylko aby uniknąć jakże wspólne R_GlobalEnv:

sandbox <-new.env() 

Teraz umieszczamy w nim dwie funkcje: f, która szuka zmiennej o nazwie x; i g, który definiuje lokalną x i wzywa f:

sandbox$f <- function() 
{ 
    value <- if(exists("x")) x else "not found." 
    cat("This is function f looking for symbol x:", value, "\n") 
} 

sandbox$g <- function() 
{ 
    x <- 123 
    cat("This is function g. ") 
    f() 
} 

technicyzacji: wprowadzenie definicji funkcji w konsoli powoduje potem mieć środowisko otaczającą ustawiony R_GlobalEnv, więc ręcznie wymusić Obudowy f i g dopasować środowisko, w którym "należą":

environment(sandbox$f) <- sandbox 
environment(sandbox$g) <- sandbox 

Wywołanie g. Zmienna lokalna x=123 nie zostanie znaleziony przez f:

> sandbox$g() 
This is function g. This is function f looking for symbol x: not found. 

Teraz tworzymy x w globalnym środowisku i nazywają g.Funkcja f będzie szukać x pierwszy w piaskownicy, a następnie w rodzica piaskownicy, co dzieje się R_GlobalEnv:

> x <- 456 
> sandbox$g() 
This is function g. This is function f looking for symbol x: 456 

Wystarczy sprawdzić, że f szuka x pierwszy w swojej obudowie, możemy umieścić x tam i wywołać g:

> sandbox$x <- 789 
> sandbox$g() 
This is function g. This is function f looking for symbol x: 789 

Wniosek: sprawdzania symboli R w następujący łańcuch środowiskach obejmujących, nie ramki utworzony podczas wykonywania oceny funkcji zagnieżdżonej połączenia.

EDIT: po prostu dodając link do this very interesting answer from Martin Morgan on the related subject of parent.frame() vs parent.env()

+0

To jest najlepsza ilustracja, którą widziałem. Dziękuję bardzo! Nie rozumiałem różnicy środowisk i ramek. – dayne

2

można użyć zamknięcia:

f2 <- function(...){ 
    f1 <- function(...){ 
    print(var1) 
    } 
    var1 <- "hello" 
    f1(...) 
} 
f2() 
+0

Dobrze, ale muszę mieć możliwość korzystania z funkcji wewnętrznej jako samodzielnej funkcji. Nie chciałem na nowo definiować funkcji wewnętrznej za każdym razem, gdy nazywam ją funkcją zewnętrzną (nie wspominając o duplikowaniu wiązki kodu). – dayne

+0

To najczystsze ustawienie w mojej opinii: umieść wszystkie dane na liście (my_data), a następnie podaj je jako argument do swojej funkcji. Wewnątrz funkcji można użyć z (my_data, {}), aby uniknąć dodatkowego pisania. –