2016-03-23 11 views
6

Ho problemi a far funzionare un ciclo while(TRUE). Questo esempio credo che ottiene al nocciolo del problema:mentre TRUE + interrompono un sottosistema

l <- list(x = 5) 

while (TRUE){ 
    with(l, if (x > 100) break else l$x <<- x + 5) 
} 

che corre con l'errore:

Error in eval(expr, envir, enclos) :

no loop for break/next, jumping to top level

Stranamente, il ciclo while sembra aver eseguito con successo:

l 
# $x 
# [1] 105 

E ' sembra che il problema è che sto inviando l'istruzione break in un sottoambiente, poiché il seguente funziona come previsto senza errori:

x = 5 

while(TRUE){ 
    if (x > 100) break else x <<- x+5 
} 

pensando che sia solo un problema ambienti, ho anche provato a sostituire break con eval(break, parent.env()) e eval(break, parent.frame()), senza alcun risultato.

Come posso interrompere questo errore?


suppongo sessionInfo() possono essere rilevanti:

R version 3.2.4 (2016-03-10) 
Platform: x86_64-pc-linux-gnu (64-bit) 
Running under: Ubuntu 14.04.3 LTS 

locale: 
[1] LC_CTYPE=en_US.UTF-8  LC_NUMERIC=C    LC_TIME=en_US.UTF-8  LC_COLLATE=en_US.UTF-8  
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 LC_PAPER=en_US.UTF-8  LC_NAME=C     
[9] LC_ADDRESS=C    LC_TELEPHONE=C    LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C  

attached base packages: 
[1] stats  graphics grDevices utils  datasets methods base  

other attached packages: 
[1] readxl_0.1.0.9000 data.table_1.9.6 haven_0.2.0  

loaded via a namespace (and not attached): 
[1] rsconnect_0.4.1.11 tools_3.2.4  Rcpp_0.12.1  chron_2.3-47  
+0

C'è un motivo per usare 'with'? Perché non solo 'if (l $ x> 100) rompere else l $ x <-l $ x + 100'? – nicola

+0

@nicola il contesto del problema è leggermente diverso; Ho scelto questo per replicare ciò che ritengo essere l'essenziale. Detto questo, nel mio uso attuale, 'break' viene inviato all'interno di una chiamata a' [.data.table' – MichaelChirico

+0

Forse sarebbe meglio spiegare il contesto sarebbe utile, poiché qui è chiaro un problema di ambiente: 'while (TRUE) con (l, break) 'produce lo stesso errore. Apparentemente 'with' produce un ambiente dal quale R non si aspetta di rompere. – nicola

risposta

3

Una variante di @ Il suggerimento di Vongo è di catturare l'ambiente in cui si suppone che la valutazione si verifichi con environment() e quindi utilizzare evalq() per avere lo break valutato nel posto giusto.

l <- list(x = 5) 
while (TRUE){ 
    env <- environment() 
    with(l, if (x > 100) evalq(break, env) else l$x <<- x + 5) 
} 

Ciò evita l'analisi di una stringa di testo e, per tale motivo, sembra meno hacky. La valutazione nell'ambiente catturato env consente al loop di essere a qualsiasi livello nel codice R, non solo in .GlobalEnv. Questo è come un salto non locale (noto come GOTO) che rende più difficile ragionare sul codice.

+0

anche 'evalq (break, parent.env (environment()))'. Non sono ancora sicuro del perché il codice originale non fosse un ciclo infinito se 'break' è stato inviato nell'ambiente sbagliato. Qualche ragione? – MichaelChirico

+0

@MichaelChirico meglio essere espliciti su env = piuttosto che fare affidamento sull'ordine dello stack di chiamate per l'individuazione, poiché il programmatore potrebbe inavvertitamente modificare lo stack di chiamate.Il codice originale ha generato un errore e, come conseguenza dell'errore, il ciclo è terminato. –

+0

Ohh che ha perfettamente senso. – MichaelChirico

1

Forse non capisco tutti i pali del problema, ma si potrebbe provare:

l <- list(x = 5) 
while (TRUE){ 
    with(l, if (x > 100) eval(parse(text="break"), envir=.GlobalEnv) else l$x <<- x + 5) 
} 
+0

Scusa, sto tornando . Penso che @ Martin Morgan abbia spiegato questo approccio e persino migliorato. Complimenti! – Vongo

Problemi correlati