2012-09-26 24 views
36

Estoy desarrollando un paquete en R. Tengo un montón de funciones, algunas de ellas necesitan algunas variables globales. ¿Cómo administro variables globales en paquetes?Variables globales en paquetes en R

He leído algo sobre el medio ambiente, pero no entiendo cómo funcionará, si esta es la manera de hacerlo.

+0

¿Podría profundizar más en su situación específica? Entonces podríamos ayudarlo a encontrar alternativas, preferiblemente ... –

+0

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

Respuesta

9

En general, las variables globales son evil. El principio subyacente de por qué son malvados es que quieres minimizar las interconexiones en tu paquete. Estas interconexiones a menudo hacen que las funciones tengan efectos secundarios, es decir, depende no solo de los argumentos de entrada cuál es el resultado, sino también del valor de alguna variable global. Especialmente cuando la cantidad de funciones crece, esto puede ser difícil de resolver y mucho menos depurar.

Para las variables globales en R, vea este SO post.

Editar en respuesta a tu comentario: Una alternativa podría ser simplemente pasar la información necesaria a las funciones que lo necesitan. Se puede crear un nuevo objeto que contiene esta información:

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

y requieren todas las funciones que necesitan esta información para tenerlo como un argumento:

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

De esta manera queda claro a partir del código que se necesita información de token en la función, y puede depurar la función por sí mismo. Además, la función no tiene efectos secundarios, ya que su comportamiento está completamente determinado por sus argumentos de entrada. Un script de usuario típico sería algo así como:

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

Espero que esto aclare las cosas.

+2

Gracias por la respuesta. Tengo experiencia en programación y sé que las variables globales generalmente son un nogo. Sin embargo, estoy estableciendo un acceso API a un servicio, para permanecer conectado a este servicio, las funciones necesitan un par de tokens. Estos tokens deben ser accesibles por todas las funciones, lo que se me ocurre es crear un archivo .RData que almacene estos datos, pero parece un mal idear. – Rimbaud

+3

El patrón R normal es tener algún tipo de objeto 'manejar' que mantenga sus tokens, y pasar ese control a sus funciones. Eso también le permite tener múltiples sesiones concurrentes con diferentes tokens. Ese es el patrón para el acceso a la base de datos, por ejemplo. – Spacedman

+0

@Spacedman +1 eso es exactamente lo que pensé –

13

También podemos establecer un option, por ejemplo

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

¿Dónde exactamente se almacenará esto? – Rimbaud

+0

@Rimbaud En una pausa llamada '.Options' ubicada en el paquete' base'. – James

+0

Esto se almacena en una lista de opciones globales para la sesión R en la que se carga el paquete. Ver '? Opciones'. –

2

La pregunta es clara:

  • Sólo un proceso de I o de varios?

  • ¿Solo en un host o en varias máquinas?

  • ¿Hay acceso de archivos común entre ellos o no?

En orden creciente de complejidad, que haría uso de un archivo, un backend SQLite a través del paquete de RSQlite o (mi favorito :) el paquete rredis para establecer/leyendo de una instancia Redis.

37

Puede usar variables locales del paquete a través de un entorno. Estas variables estarán disponibles para múltiples funciones en el paquete, pero no (fácilmente) accesibles para el usuario y no interferirán con el espacio de trabajo de los usuarios. Un ejemplo rápido y sencillo es:

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

Esta es una práctica útil, a la que agregaría que, como medida de seguridad al crear entornos como contenedores variables, generalmente se debe establecer el entorno padre en 'emptyenv()', para protegerse contra recoger accidentalmente valores más arriba en el ruta de búsqueda: así 'new.env (parent = emptyenv())', en lugar de simplemente 'new.env()'. – egnha