Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

while TRUE + break in a sub-environment

I'm having trouble getting a while(TRUE) loop to work. This example I think gets at the crux of the problem:

l <- list(x = 5)

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

Which runs with error:

Error in eval(expr, envir, enclos) :

no loop for break/next, jumping to top level

Strangely, the while loop appears to have executed successfully:

l
# $x
# [1] 105

It appears the problem is I'm sending the break statement in a sub-environment, since the following works as expected without error:

x = 5

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

Thinking it's just an environments issue, I also tried replacing break with eval(break, parent.env()) and eval(break, parent.frame()), to no avail.

How can I stop this error?


I suppose sessionInfo() may be relevant:

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      
like image 832
MichaelChirico Avatar asked Mar 23 '16 15:03

MichaelChirico


2 Answers

A variant of @Vongo's suggestion is to capture the environment in which the evaluation is supposed to occur with environment(), and then use evalq() to have the break evaluated in the right place.

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

This avoids parsing a text string and seems for that reason less hacky. Evaluating in the the captured environment env allows the loop to be at any level in R code, not just in .GlobalEnv. This is like a non-local jump (aka GOTO) which makes it more difficult to reason about the code.

like image 77
Martin Morgan Avatar answered Nov 14 '22 22:11

Martin Morgan


Maybe I don't understand all the stakes of the problem, but you could try :

l <- list(x = 5)
while (TRUE){
  with(l, if (x > 100) eval(parse(text="break"), envir=.GlobalEnv) else l$x <<- x + 5)
}
like image 37
Vongo Avatar answered Nov 14 '22 23:11

Vongo