2012-09-26 28 views
36

Sto sviluppando un pacchetto in R. Ho un sacco di funzioni, alcune hanno bisogno di alcune variabili globali. Come gestisco le variabili globali nei pacchetti?Variabili globali nei pacchetti in R

Ho letto qualcosa sull'ambiente, ma non capisco come funzionerà, se questa è la via da seguire per le cose.

+0

Potresti approfondire la tua situazione specifica? Allora potremmo aiutarti a trovare alternative, preferibilmente ... –

+0

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

risposta

9

In generale le variabili globali sono male. Il principio sottostante per cui sono malvagi è che vuoi minimizzare le interconnessioni nel tuo pacchetto. Queste interconnessioni spesso fanno sì che le funzioni abbiano effetti collaterali, ovvero dipende non solo dagli argomenti di input, dal risultato, ma anche dal valore di alcune variabili globali. Soprattutto quando cresce il numero di funzioni, questo può essere difficile da ottenere correttamente e l'inferno da eseguire il debug.

Per le variabili globali in R vedere questo SO post.

Modifica in risposta al tuo commento: Un'alternativa potrebbe essere quella di passare semplicemente le informazioni necessarie alle funzioni che ne hanno bisogno. È possibile creare un nuovo oggetto che contiene queste informazioni:

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

e richiedono tutte le funzioni che hanno bisogno queste informazioni per avere come argomento:

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

In questo modo è chiaro dal codice che le informazioni sui token sono necessarie nella funzione e puoi eseguire il debug della funzione da solo. Inoltre, la funzione non ha effetti collaterali, poiché il suo comportamento è completamente determinato dai suoi argomenti di input. Uno script utente tipico sarebbe simile:

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

Spero che questo rende le cose più chiare.

+2

Grazie per la risposta. Ho esperienza con la programmazione e so che le variabili globali generalmente sono un nogo. Tuttavia, sto stabilendo un accesso API a un servizio, per rimanere connesso a questo servizio, le funzioni richiedono un paio di token. Questi token dovrebbero essere accessibili da tutte le funzioni, quello che mi è venuto in mente, è la creazione di un file .RData che memorizza questi dati, ma sembra un cattivo idear. – Rimbaud

+3

Il normale pattern R è quello di avere un qualche tipo di oggetto "handle" che mantiene i token e passa tale handle alle tue funzioni. Ciò consente anche di avere più sessioni simultanee con token diversi. Questo è il modello per l'accesso al database, per esempio. – Spacedman

+0

@Spacedman +1 è esattamente quello che pensavo –

13

È possibile impostare un option, ad esempio

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

Dove verrà memorizzato esattamente? – Rimbaud

+0

@Rimbaud In un pairlist chiamato '.Options' situato nel pacchetto' base'. – James

+0

Questo è memorizzato in un elenco di opzioni globali per la sessione R in cui è caricato il pacchetto. Vedi "Opzioni". –

2

La domanda è chiara:

  • Solo un processo di R o più?

  • Solo su un host o su più macchine?

  • C'è un accesso file comune tra loro o no?

in ordine crescente di complessità, userei un file, un backend SQLite tramite il pacchetto RSQlite o (la mia :) il pacchetto preferito rredis per impostare/leggere da un'istanza Redis.

37

È possibile utilizzare le variabili locali del pacchetto attraverso un ambiente. Queste variabili saranno disponibili per più funzioni nel pacchetto, ma non (facilmente) accessibili all'utente e non interferiranno con l'area di lavoro degli utenti. Un esempio semplice e veloce è:

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

Questa è una pratica utile, a cui aggiungerei che, come misura di sicurezza quando si creano ambienti come contenitori variabili, si dovrebbe in genere impostare l'ambiente genitore su 'emptyenv()', per proteggersi dall'accumulo accidentale di valori più in alto nel percorso di ricerca: quindi 'new.env (parent = emptyenv())', invece di solo 'new.env()'. – egnha