什么时候需要在 if 语句周围加括号来控制 R 中公式的顺序?

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

我试图用一个数除以两个 if 语句的结果之和。出于某种原因,R 在完成第一个 if 语句后忽略两个 if 语句周围的括号,并仅对第一个 if 语句进行除法。在第一个 if 语句周围添加圆括号时,公式会按预期工作。 问题是:为什么会这样?

用 ifelse(y==2,4,1) 替换 if 语句解决了它,以及额外的括号。我很好奇为什么第一次测试给了我意想不到的结果。

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

我希望 test1 和 test2 的结果都是 0.5

第二个语句 returns z/(4+4),因此 0.5 并且对于第一个语句,它仅评估第一个 if() 子句。证明:

> z/(if(y==2){4}else{1}+if(x==1){4}else{3})
[1] 1

一个很好的问题。来自 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.

此外,在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

您遇到的问题是 {1}+if(x==1){4}else{1} 是一个有效的语句,因此 R 将其解释为 statement3。换句话说,从 else 开始,任何内容(在 内)直到换行符或分号仅在 if 语句为 FALSE 时才会遇到。

通常情况下,类似

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

我们知道在最后一个大括号之后,if 语句已经完成,但表示表达式结束的是 换行符,而不是右大括号。例如,这不会创建 a

if (y == 2) {
  4
} else {
  1
} -> a

另一种看待它的方式:对我来说,归结为以下事实:

  • { 用于对语句进行分组,而不是分隔语句。
  • 它不是控制流结构或函数定义语法的一部分,就像括号一样,尽管标准用法通常看起来像它。
  • 本质上,控制流符号分隔语句,因此优先级低于任何运算符,请参阅:
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

reprex package (v0.3.0)

于 2019-08-19 创建

从这个角度来看,结果并不令人惊讶。

为了进一步说明,使用 OP 的案例:

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

reprex package (v0.3.0)

于 2019-08-19 创建

这个问题引发了关于 R-devel 邮件列表的讨论, https://r.789695.n4.nabble.com/Documenting-else-s-greed-td4758844.html.

R 处理这个问题的方式令人惊讶,因为在 if/else 和其他一些构造中,左大括号(如果有的话)是要计算的表达式的一部分,而不是语法定界符。特别是,表达式不一定以右大括号结尾。例如,{2} + 32 + 3 是等价的表达式,所以下面的第一个命令给出 0,而不是 3(第二个命令的结果也是 0,正如预期的那样)。

if(TRUE) 0 else {2} + 3
## [1] 0
if(TRUE) 0 else 2 + 3
## [1] 0

这种混淆主要出现在'if'结构的'else'部分,但不限于此。例如,出于同样的原因({x^2} + 1 等同于 x^2 + 1),这定义了一个主体为 x^2 + 1 的函数,尽管可能会在下面的第一行出现错误:

f <- function(x){x^2} + 1
f(2)
## [1] 5