Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When do I need parentheses around an if statement to control the sequence of a formula in R?

I am trying to divide a number by the sum of the result of two if statements. For some reason, R ignores the parentheses around both the if statements after completing the first if statement and does the division on just the first if statement. When adding parentheses around the first if statement, the formula works as expected. Question is: why is that?

Replacing the if statements with ifelse(y==2,4,1) solves it, as well as the extra parentheses. I am curious why the first test gives me the unexpected result.

x <- 1
y <- 2 
z <- 4

test1 <- z/(if(y==2){4}else{1}+if(x==1){4}else{1})
> print(test1)

[1] 1

test2 <- z/((if(y==2){4}else{1})+if(x==1){4}else{1})
> print(test2)

[1] 0.5

I would expect the outcome of test1 and test2 both to be 0.5

like image 342
Berend Avatar asked Aug 16 '19 15:08

Berend


3 Answers

An excellent question. From the R Language Definition:

Computation in R consists of sequentially evaluating statements. Statements, such as x<-1:10 or mean(y), can be separated by either a semi-colon or a new line.

Further, under if:

The if/else statement conditionally evaluates two statements. There is a condition which is evaluated and if the value is TRUE then the first statement is evaluated; otherwise the second statement will be evaluated. The if/else statement returns, as its value, the value of the statement that was selected. The formal syntax is

if ( statement1 )
    statement2
else
    statement3

The issue you encountered is that {1}+if(x==1){4}else{1} is a valid statement so R interprets this as statement3. In other words, from else, anything (within the block) up until a newline or a semicolon is only encountered when the if statement is FALSE.

Normally, in something like

if (y == 2) {
  4
} else {
  1
}

we understand that after the final brace, the if statement has finished, but it is the newline, not the closing brace, that signifies the end of the expression. For example, this does not create a

if (y == 2) {
  4
} else {
  1
} -> a
like image 124
Hugh Avatar answered Oct 22 '22 23:10

Hugh


Second statement returns z/(4+4), ergo 0.5 and for the first statement, it evaluates only the first if() clause. Proof:

> z/(if(y==2){4}else{1}+if(x==1){4}else{3})
[1] 1
like image 2
Roman Luštrik Avatar answered Oct 22 '22 23:10

Roman Luštrik


Another way to look at it : to me it boils down to the fact that :

  • { is used to group statements, not delimit them.
  • It is not part of the syntax of control flow constructs or function definition, as parentheses are, despite standard usage often looking like it is.
  • By nature, control flow symbols delimit statements and thus have lower precedence than any operator, see:
lobstr::ast(if (TRUE) 1 else 0 + 4)
#> o-`if` 
#> +-TRUE 
#> +-1 
#> \-o-`+` 
#>   +-0 
#>   \-4
lobstr::ast(if (TRUE) 1 else {0} + 4)
#> o-`if` 
#> +-TRUE 
#> +-1 
#> \-o-`+` 
#>   +-o-`{` 
#>   | \-0 
#>   \-4
lobstr::ast(if (TRUE) 1 else {0} <- 4) # `<-` has very low precedence amongst operators
#> o-`if` 
#> +-TRUE 
#> +-1 
#> \-o-`<-` 
#>   +-o-`{` 
#>   | \-0 
#>   \-4
lobstr::ast(if (TRUE) 1 else {0} ? 4)  # `?` has the lowest precedence amongst operators
#> o-`if` 
#> +-TRUE 
#> +-1 
#> \-o-`?` 
#>   +-o-`{` 
#>   | \-0 
#>   \-4

Created on 2019-08-19 by the reprex package (v0.3.0)

In this light the result is not that surprising.

To illustrate further, using OP's case :

lobstr::ast(z/(if(y==2){4}else{1}+if(x==1){4}else{1}))
#> o-`/` 
#> +-z 
#> \-o-`(` 
#>   \-o-`if` 
#>     +-o-`==` 
#>     | +-y 
#>     | \-2 
#>     +-o-`{` 
#>     | \-4 
#>     \-o-`+` 
#>       +-o-`{` 
#>       | \-1 
#>       \-o-`if` 
#>         +-o-`==` 
#>         | +-x 
#>         | \-1 
#>         +-o-`{` 
#>         | \-4 
#>         \-o-`{` 
#>           \-1

Created on 2019-08-19 by the reprex package (v0.3.0)

like image 1
Moody_Mudskipper Avatar answered Oct 23 '22 01:10

Moody_Mudskipper