I have the following code in the script test.R:
if (x==2){
stop("the script ends")
}
Now I source this script
source(test.R)
t <- 2
I would like the code to stop if x==2 and does not go further. However, it continues and assigns t <- 2. I can use the function warnings(options) but I want to avoid this option and implement a condition within the if. Any suggestion?
The code you list should work as expected.
As an example, I made two scripts, test.R and test2.R:
1. File test.R:
if (identical(x, 2)) {
stop("the script ends")
}
(Note: I'm using identical(x, 2) as the safer way to check whether x equals 2, but x == 2 would work the same in this example.)
2. File test2.R:
x <- 1
source("test.R")
t <- 1
print("This should be printed.")
x <- 2
source("test.R")
t <- 2
print("This should not be printed!")
Now I run test2.R from the console:
> t <- 5
> source('test2.R')
[1] "This should be printed."
Error in eval(ei, envir) : the script ends
> t
[1] 1
We see that the check passed the first time, when x == 1, and it failed the second time, when x == 2. Therefore, the value of t at the end is 1, because the first assignment was run and the second was not.
A while loop can create a condition that you can escape from as you are suggesting:
while (TRUE){
if (x==2) {
break
}
}
This is assuming that your code is 'all the way to the left' when executing. Seeing a little more might help, or better understanding how x is being set or being used. Note that using something like while(TRUE) might not be best practice, and can lead to infinite execution if you do not exit properly.
Related
I am debugging a function in R using browser() and have encountered something that I don't understand. Below is a simple example function:
testf<-function(x)
{
if(x==1) x=x+1
return(x)
}
If I run this it behaves as expected:
> testf(1)
[1] 2
> testf(2)
[1] 2
Now, I insert a browser() function to enter debug mode:
testf<-function(x)
{
browser()
if(x==1) x=x+1
return(x)
}
If I now run testf(1) and use the Next command feature in debug mode to step through the function, it produces the expected output of 2. However, if I run the if statement directly (e.g. by highlighting and pushing the Run button), x does not get incremented:
Browse[1]> x
[1] 1
Browse[1]> if(x==1) x=x+1
debug at #3: x = x + 1
Browse[3]> x
[1] 1
If I instead run x=x+1 by itself then x does get incremented:
Browse[3]> x
[1] 1
Browse[3]> x=x+1
Browse[3]> x
[1] 2
Browse[3]>
Then x does get incremented as expected.
Why? My understanding of debug mode is that you can run any command and it will get executed as if you were running the function, but this seems not to be the case with the if statement above.
What am I missing here?
The if condition works in two separate steps :
evaluate condition
execute statement
Perhaps easier to visualize what happens this way:
testf <- function(x)
{
browser()
if(x==1) {
x=x+1
}
return(x)
}
I am going to provide a more detailed answer to my question, based on Waldi's answer above. The real heart of my question is this: Why does my if statement perform differently in normal and debug modes?
> x=1
> if(x==1) x=x+1
> x
[1] 2
> testf(1)
Called from: testf(1)
Browse[1]> x
[1] 1
Browse[1]> if(x==1) x=x+1
debug at #3: x = x + 1
Browse[3]> x
[1] 1
The answer can be seen by using the debugger to step through the function. When I use the Next tool to step through the if, it takes two steps to get through rather than the one step that we might expect. This is because the if involves two steps:
Evaluate condition
Execute command.
Normally, these are executed together at one time. However, the debugger separates them. This is presumably because it is possible that an error could be in either the evaluation step or the execution step. This is sensible, but it has the side effect that if I run the if at the command line in debug mode, it only executes the first part (evaluate) and not the second part (execute).
How does return() in a with() block work?
Here is a test function
test_func <- function(df, y) {
with(df,
if(x > 10){
message('Inside step 1')
return(1)
}
)
message("After step 1")
if(y > 10){
message('In side step 2')
return(2)
}
message("After step 2")
}
The function keeps going after return(1).
df <- data.frame(x = 11)
y <- 11
test_func(df, y) ## result is 2
Output
Inside step 1
After step 1
In side step 2
[1] 2
return(1) doesn't return to the test_func() rather than get out the with() block
df <- data.frame(x = 11)
y <- 5
test_func(df, y) ## no result
Output
Inside step 1
After step 1
After step 2
I think the main point here is that return() is designed to exit the current scope to the parent scope with a particular value. In the case of running
return("hello")
# Error: no function to return from, jumping to top level
You get an error because we are calling this from the global environment and there is no parent scope you are jumping back to. Note that thanks to R's lazy evaluation, parameters passed to a function are usually evaluated in the environment where they were passed from. So in this example
f <- function(x) x
f(return("hello"))
# Error in f(return("hello")) :
# no function to return from, jumping to top level
So because we are actually passing in the return() call to the function from the global environment, that's where the return will be evaluated and will return the same error. But note that this is not what with does, instead it captures the commands you pass and re-runs them in a new environment. It's closer to something like this
f <- function(x) eval(substitute(x))
f(return("hello"))
# [1] "hello"
This eval() creates a new level of scope we can escape from, and because we aren't evaluating the parameter passed to the function directly, we are just running those commands in a different environment, the return is no longer evaluated in the global environment so there's no error. So the function we created is basically the same as calling
with(NULL, return("hello"))
# [1] "hello"
which is different from something like
print(return("hello"))
# no function to return from, jumping to top level
where the parameter is directly evaluated. So the different meanings of return() really are side effects of non-standard evaluation in this case.
When return() is used inside a with(), you are not returning from the function that you called with() from, but you are returning from the scope that with() created for you to run your command.
How to fix this particular problem is already addressed by the comments left by #IceCreamToucan. You just need to move the return outside of the with().
I use data checks in my code, for example the following one:
if (...)
stop(paste('Warning: Weights do not sum up to 1')
The problem is that if the condition is true and the warning appears in the console, the code does not stop running. If hidden in a long code, one needs to scroll up always in the console output to see if a warning appeared.
Is there a way to tell R to interupt ALL code when the stop warning appears and the condition is true? Something like BREAK ?
I do not represent a reproducible example here because my code is quite long.
EDIT:
Here is a mini example:
When I execute
a=1+2
if (a==3)
stop('a equals 3')
b=4
1+1
I would like to stop after printing
> a=1+2
>
> if (a==3)
+ stop('a equals 3')
Error: a equals 3
but R executes everything, also the last part:
> a=1+2
>
> if (a==3)
+ stop('a equals 3')
Error: a equals 3
>
>
> b=4
>
> 1+1
[1] 2
According to stop, it only stops evaluation of the current expression. While I agree with Roland's comment to encapsulate your code into meaningful pieces via functions, a quick hack would be to wrap all your current code in curly braces. That will make it appear to the R parser as a single expression.
R> # without curly braces
R> x <- 1
R> y <- 2
R> if (x < y)
+ stop("x < y")
Error: x < y
R> print("hello")
[1] "hello"
R>
R> # with curly braces
R> {
+ x <- 1
+ y <- 2
+ if (x < y)
+ stop("x < y")
+ print("hello")
+ }
Error: x < y
If I understand you correctly, it seems not to be the default behaviour of R.
So I think there is some change of the stop handler. You can try to call options(stop=NULL) before running your code. See the doc:
The default behaviour (the NULL error-handler) in interactive use is
to return to the top level prompt or the top level browser, and in
non-interactive use to (effectively) call q("no", status = 1, runLast
= FALSE).
At least the following code worked in my case (R 3.2.1):
funErr <- function() {
if (TRUE)
stop("now")
print("after stop")
}
funCaller <- function() {
print("before call")
funErr()
print("after call")
}
print("before global")
# Also possible to uncomment the following to test the `stop()`
# inside of `funErr()`
if(TRUE)
stop("global error")
funCaller()
I'm looking into having some user interaction in my code using RStudio. However, I'm running into some issues with the behavior of the command line in conjunction with the readline() function. Here's an example to illustrate my issue:
x <- 2
y <- 2
if (x == 2) { x <- readline("Put your x here: ")
} else { x <- 3 }
if (y == 2) { print("Something.")}
If I highlight this and hit "run" (or ctrl-alt-b at the end) to run line by line in the command line, I run into unexpected behavior shown here in the command line:
> x <- 2
> y <- 2
> if (x == 2) { x <- readline("Put your x here: ")
+ } else { x <- 3 }
Put your x here: if (y == 2) { print("Something.")}
Notice that on line 5, it skips right over the "Put your x here: " prompt (I did not input anything) and continues to the next line. Please do also note, however, when I "source" this code, the issue does not happen and the command line runs as expected (prompts me and waits until I input something).
Overall, this is a minor issue, but for testing purposes, it would be a major help to be able to run my user interaction code without having to source the entire file. Thanks for in advance!
I was wondering about how to write do-while-style loop?
I found this post:
you can use repeat{} and check conditions whereever using if() and
exit the loop with the "break" control word.
I am not sure what it exactly means. Can someone please elaborate if you understand it and/or if you have a different solution?
Pretty self explanatory.
repeat{
statements...
if(condition){
break
}
}
Or something like that I would think. To get the effect of the do while loop, simply check for your condition at the end of the group of statements.
See ?Control or the R Language Definition:
> y=0
> while(y <5){ print( y<-y+1) }
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
So do_while does not exist as a separate construct in R, but you can fake it with:
repeat( { expressions}; if (! end_cond_expr ) {break} )
If you want to see the help page you cannot type ?while or ?repeat at the console but rather need to use ?'repeat' or ?'while'. All the "control-constructs" including if are on the same page and all need character quoting after the "?" so the interpreter doesn't see them as incomplete code and give you a continuation "+".
Building on the other answers, I wanted to share an example of using the while loop construct to achieve a do-while behaviour. By using a simple boolean variable in the while condition (initialized to TRUE), and then checking our actual condition later in the if statement. One could also use a break keyword instead of the continue <- FALSE inside the if statement (probably more efficient).
df <- data.frame(X=c(), R=c())
x <- x0
continue <- TRUE
while(continue)
{
xi <- (11 * x) %% 16
df <- rbind(df, data.frame(X=x, R=xi))
x <- xi
if(xi == x0)
{
continue <- FALSE
}
}
Noticing that user 42-'s perfect approach {
* "do while" = "repeat until not"
* The code equivalence:
do while (condition) # in other language
..statements..
endo
repeat{ # in R
..statements..
if(! condition){ break } # Negation is crucial here!
}
} did not receive enough attention from the others, I'll emphasize and bring forward his approach via a concrete example. If one does not negate the condition in do-while (via ! or by taking negation), then distorted situations (1. value persistence 2. infinite loop) exist depending on the course of the code.
In Gauss:
proc(0)=printvalues(y);
DO WHILE y < 5;
y+1;
y=y+1;
ENDO;
ENDP;
printvalues(0); # run selected code via F4 to get the following #
1.0000000
2.0000000
3.0000000
4.0000000
5.0000000
In R:
printvalues <- function(y) {
repeat {
y=y+1;
print(y)
if (! (y < 5) ) {break} # Negation is crucial here!
}
}
printvalues(0)
# [1] 1
# [1] 2
# [1] 3
# [1] 4
# [1] 5
I still insist that without the negation of the condition in do-while, Salcedo's answer is wrong. One can check this via removing negation symbol in the above code.