2012-09-26 6 views
36

Pracuję nad pakietem w języku R. Mam wiele funkcji, niektóre z nich wymagają pewnych zmiennych globalnych. Jak zarządzać zmiennymi globalnymi w pakietach?Zmienne globalne w pakietach w R

Czytałem coś o środowisku, ale nie rozumiem, jak to zadziała, jeśli nawet to jest sposób na poruszanie się.

+0

Mógłbyś rozwinąć bardziej od konkretnej sytuacji? Wtedy pomożemy Ci znaleźć alternatywy, najlepiej ... –

+0

http://stackoverflow.com/questions/5526322/examples-of-the-perils-of-globals-in-r-and-stata –

Odpowiedz

9

Ogólnie zmienne globalne to Zło. Podstawową zasadą, dlaczego są złe, jest to, że chcesz zminimalizować połączenia w pakiecie. Te wzajemne połączenia często powodują, że funkcje mają skutki uboczne, tj. Zależy nie tylko od argumentów wejściowych, jaki jest wynik, ale także od wartości niektórych zmiennych globalnych. Zwłaszcza, gdy liczba funkcji rośnie, może być trudno uzyskać prawo, a piekło do debugowania.

Dla zmiennych globalnych w R zobacz ten SO post.

Edytuj w odpowiedzi na komentarz: Alternatywą może być po prostu przekazanie potrzebnych informacji do funkcji, które tego wymagają. Można utworzyć nowy obiekt, który zawiera tę informację:

token_information = list(token1 = "087091287129387", 
         token2 = "UA2329723") 

i wymagają wszystkie funkcje, które potrzebują tych informacji, aby mieć go jako argumentu:

do_stuff = function(arg1, arg2, token) 
do_stuff(arg1, arg2, token = token_information) 

W ten sposób wynika z kodu informacje o tokenach są potrzebne w funkcji i możesz samodzielnie debugować tę funkcję. Ponadto funkcja nie ma skutków ubocznych, ponieważ jej zachowanie jest w pełni zdeterminowane przez argumenty wejściowe. Typowy skrypt użytkownika wyglądałby tak:

token_info = create_token(token1, token2) 
do_stuff(arg1, arg2, token_info) 

Mam nadzieję, że to wyjaśni sprawę.

+2

Dzięki za odpowiedź. Mam doświadczenie w programowaniu i wiem, że zmienne globalne są generalnie nogo. Jednak tworzę dostęp API do usługi, aby pozostać w kontakcie z tą usługą, funkcje wymagają kilku tokenów. Te żetony powinny być dostępne dla wszystkich funkcji, co wymyśliłem, to tworzenie pliku .RData, który przechowuje te dane, ale to wydaje się złym idearem. – Rimbaud

+3

Normalny wzorzec R to jakiś obiekt "uchwytowy", który utrzymuje twoje żetony i przekazuje ten uchwyt do twoich funkcji. Pozwala to również na równoległe sesje z różnymi tokenami. To na przykład wzorzec dostępu do bazy danych. – Spacedman

+0

@Spacedman +1 to jest dokładnie to, co myślałem –

13

Można ustawić option np

options("mypkg-myval"=3) 
1+getOption("mypkg-myval") 
[1] 4 
+1

Gdzie dokładnie będzie to przechowywane? – Rimbaud

+0

@Rimbaud W parze nazwanej '.Options' znajdującej się w pakiecie' base'. – James

+0

Jest to przechowywane na globalnej liście opcji sesji R, w której pakiet jest ładowany. Zobacz '? Options'. –

2

Pytanie jest jasne:

  • Tylko jeden proces R lub kilka?

  • Tylko na jednym hoście lub na kilku maszynach?

  • Czy jest wśród nich wspólny dostęp do plików, czy nie?

rosnące złożoności, chciałbym użyć pliku, backend SQLite poprzez pakietu RSQlite lub (mój ulubiony :) pakietu rredis, aby ustawić/odczytu z instancji Redis.

37

Można używać zmiennych lokalnych pakietu za pośrednictwem środowiska. Zmienne te będą dostępne dla wielu funkcji w pakiecie, ale nie będą (łatwo) dostępne dla użytkownika i nie będą zakłócać obszaru roboczego użytkowników. Szybki i prosty przykład to:

pkg.env <- new.env() 

pkg.env$cur.val <- 0 
pkg.env$times.changed <- 0 

inc <- function(by=1) { 
    pkg.env$times.changed <- pkg.env$times.changed + 1 
    pkg.env$cur.val <- pkg.env$cur.val + by 
    pkg.env$cur.val 
} 

dec <- function(by=1) { 
    pkg.env$times.changed <- pkg.env$times.changed + 1 
    pkg.env$cur.val <- pkg.env$cur.val - by 
    pkg.env$cur.val 
} 

cur <- function(){ 
    cat('the current value is', pkg.env$cur.val, 'and it has been changed', 
     pkg.env$times.changed, 'times\n') 
} 

inc() 
inc() 
inc(5) 
dec() 
dec(2) 
inc() 
cur() 
+7

Jest to przydatna praktyka, do której chciałbym dodać, że jako środek bezpieczeństwa przy tworzeniu środowisk jako zmiennych kontenerów, należy ogólnie ustawić środowisko macierzyste na 'emptyenv()', aby zabezpieczyć się przed przypadkowym podniesieniem wartości wyższych w search path: stąd 'new.env (parent = emptyenv())', zamiast tylko 'new.env()'. – egnha