2013-01-18 7 views
23

Aggiornamento 2 @G. Grothendieck ha pubblicato due approcci. Il secondo sta cambiando l'ambiente della funzione all'interno di una funzione. Questo risolve il mio problema di troppi repliche di codifica. Non sono sicuro che questo sia un buon metodo per passare il controllo CRAN quando si creano i miei script in un pacchetto. Aggiornerò di nuovo quando avrò delle conclusioni.In R, come rendere le variabili all'interno di una funzione disponibile per la funzione di livello inferiore all'interno di questa funzione? (Con, allegare, ambiente)

Aggiornamento

Sto cercando di passare un sacco di variabili degli argomenti di input per f2 e non voglio indicizzare tutte le variabili all'interno della funzione come env$c, env$d, env$calls, è per questo che ho cercato di utilizzare with in f5 e f6 (modificato f2). Tuttavia, assign non funziona con with all'interno del {}, spostando assign fuori with farà il lavoro, ma nel mio caso reale ho alcune assign s all'interno delle with espressioni che non so come muoversi fuori della funzione with facilmente .

Ecco un esempio:

## In the <environment: R_GlobalEnv> 
a <- 1 
b <- 2 
f1 <- function(){ 
    c <- 3 
d <- 4 
f2 <- function(P){ 
    assign("calls", calls+1, inherits=TRUE) 
    print(calls) 
    return(P+c+d) 
} 
calls <- 0 
v <- vector() 
for(i in 1:10){ 
    v[i] <- f2(P=0) 
    c <- c+1 
    d <- d+1 
    } 
return(v) 
} 
f1() 

Funzione f2 è dentro f1, quando f2 si chiama, cerca le variabili calls,c,d nell'ambiente environment(f1). Questo è quello che volevo.

Tuttavia, quando voglio utilizzare f2 anche nelle altre funzioni, definirò questa funzione nell'ambiente Globale, chiamiamolo f4.

f4 <- function(P){ 
    assign("calls", calls+1, inherits=TRUE) 
    print(calls) 
    return(P+c+d) 
} 

Questo non funziona, perché cercherà calls,c,d nell'ambiente globale invece che all'interno di una funzione in cui viene chiamata la funzione. Ad esempio:

f3 <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
    v[i] <- f4(P=0) ## or replace here with f5(P=0) 
    c <- c+1 
    d <- d+1 
    } 
    return(v) 
} 
f3() 

Il modo sicuro dovrebbe essere definire calls,c,d negli argomenti ingresso f4 e quindi passare questi parametri in f4. Tuttavia, nel mio caso ci sono troppe variabili da passare in questa funzione f4 e sarebbe meglio che io possa passarlo come un ambiente e dire f4 non guardare nell'ambiente Globale (environment(f4)), guardare solo all'interno dello environment quando viene chiamato f3.

Il modo in cui lo risolvo ora è utilizzare l'ambiente come un elenco e utilizzare la funzione with.

f5 <- function(P,liste){ 
    with(liste,{ 
    assign("calls", calls+1, inherits=TRUE) 
    print(calls) 
    return(P+c+d) 
    } 
) 
} 
f3 <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
    v[i] <- f5(P=0,as.list(environment())) ## or replace here with f5(P=0) 
    c <- c+1 
    d <- d+1 
    } 
    return(v) 
} 
f3() 

Tuttavia, ora assign("calls", calls+1, inherits=TRUE) non funziona come dovrebbe essere dato assign non modifica l'oggetto originale. La variabile calls è collegata a una funzione di ottimizzazione in cui la funzione obiettivo è f5. Questo è il motivo per cui utilizzo assign anziché passare calls come argomenti di input. Anche l'utilizzo di attach non mi è chiaro.Qui è il mio modo per risolvere il problema assign:

f7 <- function(P,calls,liste){ 
    ##calls <<- calls+1 
    ##browser() 
    assign("calls", calls+1, inherits=TRUE,envir = sys.frame(-1)) 
    print(calls) 
    with(liste,{ 
    print(paste('with the listed envrionment, calls=',calls)) 
    return(P+c+d) 
    } 
) 
} 
######## 
################## 
f8 <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
    ##browser() 
    ##v[i] <- f4(P=0) ## or replace here with f5(P=0) 
    v[i] <- f7(P=0,calls,liste=as.list(environment())) 
    c <- c+1 
    d <- d+1 
    } 
    f7(P=0,calls,liste=as.list(environment())) 
    print(paste('final call number',calls)) 
    return(v) 
} 
f8() 

io non sono sicuro di come questo dovrebbe essere fatto in R. Sono sulla strada giusta, soprattutto quando passa attraverso il CRAN controllare? Qualcuno ha qualche suggerimento su questo?

risposta

22

(1) Passare l'ambiente del chiamante. È possibile passare esplicitamente l'ambiente principale e l'indice in esso. Prova questo:

f2a <- function(P, env = parent.frame()) { 
    env$calls <- env$calls + 1 
    print(env$calls) 
    return(P + env$c + env$d) 
} 

a <- 1 
b <- 2 
# same as f1 except f2 removed and call to f2 replaced with call to f2a 
f1a <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
     v[i] <- f2a(P=0) 
     c <- c+1 
     d <- d+1 
     } 
    return(v) 
} 
f1a() 

(2) ambiente reset chiamata di funzione è di ripristinare l'ambiente di f2b in f1b come illustrato di seguito:

f2b <- function(P) { 
    calls <<- calls + 1 
    print(calls) 
    return(P + c + d) 
} 

a <- 1 
b <- 2 
# same as f1 except f2 removed, call to f2 replaced with call to f2b 
# and line marked ## at the beginning is new 
f1b <- function(){ 
    environment(f2b) <- environment() ## 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
     v[i] <- f2b(P=0) 
     c <- c+1 
     d <- d+1 
     } 
    return(v) 
} 
f1b() 

(3) Macro utilizzando eval.parent (sostituto (...)) Un altro approccio consiste nel definire un costrutto macro-simile che inietta in modo efficace il corpo di f2c in linea in f1c1. Qui f2c corrisponde a f2b eccetto per la riga calls <- calls + 1 (non è necessario il numero <<-) e l'avvolgimento dell'intero corpo in eval.parent(substitute({...})). f1c corrisponde a f1a tranne che la chiamata a f2a viene sostituita con una chiamata a f2c.

f2c <- function(P) eval.parent(substitute({ 
    calls <- calls + 1 
    print(calls) 
    return(P + c + d) 
})) 

a <- 1 
b <- 2 
f1c <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
     v[i] <- f2c(P=0) 
     c <- c+1 
     d <- d+1 
     } 
    return(v) 
} 
f1c() 

(4) defmacro Questo è quasi lo stesso del l'ultima soluzione tranne che utilizza defmacro nel pacchetto gtools per definire la macro piuttosto che farlo noi stessi. (Vedi anche il pacchetto Rcmdr per un'altra versione defmacro.) Per il modo in cui funziona defmacro, dobbiamo anche passare calls ma dal momento che è una macro e non una funzione, basta dirgli di sostituire calls in e non è lo stesso di passare calls in un funzione.

library(gtools) 

f2d <- defmacro(P, calls, expr = { 
    calls <- calls + 1 
    print(calls) 
    return(P + c + d) 
}) 

a <- 1 
b <- 2 
f1d <- function(){ 
    c <- 3 
    d <- 4 
    calls <- 0 
    v <- vector() 
    for(i in 1:10){ 
     v[i] <- f2d(P=0, calls) 
     c <- c+1 
     d <- d+1 
     } 
    return(v) 
} 
f1d() 
+0

Questo dà il risultato che voglio ma non ho scritto chiaramente la mia richiesta. Infatti, voglio evitare di indicizzare tutte le variabili che passano in 'f2', cioè non scrivere' env $ ', ecco perché ho provato ad usare' with'. Voglio essere in grado di modificare le 'chiamate' esterne dentro' f2' ma non modificare le altre variabili nell'ambiente corrente. Aggiornerò la domanda – Zhenglei

+0

Ho aggiunto un secondo approccio. –

+0

Sembra proprio quello di cui ho bisogno. Lo metterò alla prova con il mio caso reale e aggiornerò più tardi. Grazie mille per l'aiuto! – Zhenglei

1

In generale, direi che qualsiasi variabile che è necessaria all'interno di una funzione dovrebbe essere trasmessa attraverso i suoi argomenti. Inoltre, se il suo valore è necessario in seguito, lo si restituisce dalla funzione. Non farlo può portare abbastanza rapidamente a risultati strani, ad es. cosa succede se ci sono più funzioni che definiscono una variabile x, quale dovrebbe essere usato. Se la quantità di variabili è maggiore, puoi creare una struttura dati personalizzata per questo, ad es. inserendoli in una lista nominata.

+0

Sono d'accordo con te @Paul in generale. Sto cercando di rendere i miei codici un pacchetto R, ma non posso passare facilmente il controllo CRAN con molti avvisi come i collegamenti alle variabili globali. Ci sono molti codici ripetuti perché 'f2' è definito all'interno di una funzione e voglio usarlo in un'altra nuova funzione. Mi rendo conto che il copia incolla non è una buona opzione e può causare problemi nei passaggi successivi. Voglio anche ridurre lo sforzo perché non voglio modificare troppo le Scritte già esistenti. Ecco perché provo a passare l'ambiente invece di definire una nuova struttura dati. – Zhenglei

1

Si potrebbe anche utilizzare una funzione che ridefinisce altre funzioni nell'ambiente specificato.

test_var <- "global" 

get_test_var <- function(){ 
    return(test_var) 
} 

some_function <- function(){ 
    test_var <- "local" 
    return(get_test_var()) 

} 

some_function() # Returns "global". Not what we want here... 

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

some_function2 <- function(){ 
    test_var <- "local" 
    # define function locally 
    get_test_var2 <- function(){ 
    return(test_var) 
    } 
    return(get_test_var2()) 
} 

some_function2() # Returns "local", but 'get_test_var2' can't be used in other places. 

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

add_function_to_envir <- function(my_function_name, to_envir) { 
    script_text <- capture.output(eval(parse(text = my_function_name))) 
    script_text[1] <- paste0(my_function_name, " <- ", script_text[1]) 
    eval(parse(text = script_text), envir = to_envir) 
} 

some_function3 <- function(){ 
    test_var <- "local" 
    add_function_to_envir("get_test_var", environment()) 
    return(get_test_var()) 
} 

some_function3() # Returns "local" and we can use 'get_test_var' from anywhere. 

Qui add_function_to_envir(my_function_name, to_envir) cattura lo script della funzione, analizza e rivaluta nel nuovo ambiente.

Nota: il nome della funzione per my_function_name deve essere tra virgolette.