什么时候需要 if 语句两边的括号来控制 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

我希望测试1和测试2的结果都是0.5

一个很好的问题。从R 语言定义

R 中的计算由顺序计算语句组成。语句(如 x<-1:10 或 mean(y((可以用分号或换行符分隔

此外,根据if

if/else 语句有条件地计算两个语句。有一个条件被计算,如果值为 TRUE,则计算第一个语句;否则将评估第二个语句。if/else 语句返回所选语句的值作为其值。正式语法是

if ( statement1 )
statement2
else
statement3

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

通常,在类似

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

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

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

第二条语句返回 z/(4+4(,因此返回 0.5,对于第一条语句,它只计算第一个 if(( 子句。证明:

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

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

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

创建于 2019-08-19 由 reprex 软件包 (v0.3.0(

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

为了进一步说明,使用 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

创建于 2019-08-19 由 reprex 软件包 (v0.3.0(

这个问题引发了关于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(,这定义了带有 bodyx^2 + 1的函数,尽管人们可能会在下面的第一行出现错误:

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

最新更新