Когда мне нужны круглые скобки вокруг оператора if для управления последовательностью формулы в R?

Я пытаюсь разделить число на сумму результата двух операторов if. По какой-то причине R игнорирует круглые скобки вокруг обоих операторов if после завершения первого оператора if и выполняет деление только на первый оператор if. При добавлении скобок вокруг первого оператора if формула работает так, как ожидалось. Вопрос: почему это так?

Замена операторов if на ifelse (y == 2,4,1) решает его, а также дополнительные скобки. Мне любопытно, почему первый тест дает мне неожиданный результат.

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

Ответы

Ответ 1

Отличный вопрос. Из определения языка R:

Вычисление в R состоит из последовательно оценивающих операторов. Утверждения, такие как x & lt; -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

Ответ 2

Второй оператор возвращает z/(4 + 4), эрго 0,5, а для первого оператора он оценивает только первое предложение if(). Доказательство:

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

Ответ 3

Еще один способ взглянуть на это: для меня это сводится к тому, что:

  • { используется для группировки операторов, а не их разграничения.
  • Это не является частью синтаксиса конструкций потока управления или определения функции, как в скобках, несмотря на то, что стандартное использование часто выглядит так, как есть.
  • По своей природе символы потока управления разграничивают операторы и, таким образом, имеют более низкий приоритет, чем любой оператор, см.:
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)

В этом свете результат неудивителен.

Чтобы проиллюстрировать это далее, используйте случай 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

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

Ответ 4

Этот вопрос вызвал обсуждение в списке рассылки R-devel, https://r.789695.n4.nabble.com/Documenting-else-s-greed-td4758844.html.

Способ R обрабатывает это удивительно, потому что в if/else и некоторых других конструкциях открывающие фигурные скобки, если таковые имеются, являются частью вычисляемого выражения, а не разделителями синтаксиса. В частности, выражение не обязательно заканчивается закрывающей скобкой. Например, {2} + 3 и 2 + 3 являются эквивалентными выражениями, поэтому первая команда ниже дает 0, а не 3 (результат второй также равен 0, как и ожидалось).

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

Эта путаница возникает главным образом в конструкциях 'else' из 'if', но не ограничивается ими. Например, это определяет функцию с телом x^2 + 1 по тем же причинам ({x^2} + 1 эквивалентно x^2 + 1), хотя можно ожидать ошибки в первой строке ниже:

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