Назначение значений из блоков if-else (как это работает?) - PullRequest
0 голосов
/ 02 декабря 2009

I недавно обнаружил , что вы можете условно присвоить значение с помощью блока if-else.

y <- if(condition) 1 else 2

Я понимаю, что сценарий использования для этого ограничен: если у вас есть векторизованный код, вы бы вместо этого использовали функцию ifelse. Есть выигрыш в производительности: if-else работает примерно в 35 раз быстрее, чем ifelse в скалярном случае на моей машине (хотя вам нужно вызывать его миллионы раз, чтобы заметить большую разницу).

Что меня беспокоит, так это то, что я не могу понять, почему этот код работает - я был поражен, что он не просто выдает ошибку.

Другой пример показывает, что если блоки ведут себя как функции & mdash; они автоматически возвращают последнее значение в блоке (хотя в них нельзя использовать оператор return).

y <- if(TRUE) 
{
   y <- 3
   4
}    # y is 4

Исходя из этого, я догадался, что, возможно, при вводе блока if была создана другая среда, но, похоже, это не так.

if(TRUE) sys.frames()    # NULL

Может кто-нибудь сказать мне, что происходит под капотом, пожалуйста?

Ответы [ 4 ]

8 голосов
/ 02 декабря 2009

Позвольте мне сосредоточиться на

Что меня беспокоит, так это то, что я не могу выяснить, почему этот код работает - я был поражен тем, что это не просто бросить ошибка.

Почему ты так думаешь? Что происходит, что у нас есть

  1. y присваивается выражение
  2. это выражение является if if() ...
  3. ведет к TRUE или FALSE в тесте
  4. ведет к одной из двух вводимых ветвей
  5. , приводящий к соответствующему оцениваемому коду
  6. , приводящее к конечному значению, являющемуся значением правой стороны
  7. , что приводит к присвоению этого значения y

Кажется логичным для меня.

5 голосов
/ 02 декабря 2009

Управляющие структуры, такие как if(....) ... else ..., эквивалентны вызовам функций с соответствующими (ленивыми) подвыражениями в качестве аргумента:

`if`(TRUE, "yes", "no")

В основном, if - это специальный примитив функция:

R> sapply(ls("package:base", all=TRUE), function(x) is.primitive(get(x)))["if"]
  if 
TRUE 
R> sapply(ls("package:base", all=TRUE), function(x) typeof(get(x)))["if"]
       if 
"special" 

Соответствующий раздел (Раздел 3.2.1) в руководстве R Определение языка гласит:

Потому что, если операторы / else совпадают с другими операторами, вы можете присвоить им значение. Два примера ниже эквивалентны.

R> if( any(x <= 0) ) y <- log(1+x) else y <- log(x)
R> y <- if( any(x <= 0) ) log(1+x) else log(x)
3 голосов
/ 03 декабря 2009

О второй части вашего вопроса - {} используется для группировки выражений, работает иначе, чем в определении функции. Как вы можете прочитать в help("Paren"):

Для ‘{’ - результат последнего вычисленного выражения.

Но все выражения оцениваются в текущей среде:

y <- if (TRUE) {
    print(paste("Current frame:",sys.nframe()))
    y <- 3
    z <- 5
    4
}
# [1] "Current frame: 0"
# result:
y # [1] 4
z # [1] 5

# compare to use function:
v <- if(TRUE) (function(){      
    print(paste("Current frame:",sys.nframe()))
    v <- 3
    w <- 5
    4
})()
# [1] "Current frame: 1"
v # [1] 4
w # Error: object 'w' not found

Вывод: блоки не ведут себя как функции .

редактирование: Если вы хотите использовать блоки в качестве функции, вы можете использовать local:

a <- if (TRUE) local({
    print(paste("Current frame:",sys.nframe()))
    a <- 3
    b <- 5
    4
})
# [1] "Current frame: 6"
a # [1] 4
b # Error: object 'b' not found

Тогда вы тоже можете использовать return:

a <- if (TRUE) local({
    print(paste("Current frame:",sys.nframe()))
    a <- 3
    b <- 5
    return(7)
    4
})
# [1] "Current frame: 6"
a # [1] 7
b # Error: object 'b' not found
2 голосов
/ 02 декабря 2009

Это на самом деле не очень глубоко - многие языки используют для этого конструкцию "val = x? Y: z". В R это просто свернуто в конструкцию if / else, поэтому вы пишете «val = if (x) y else z».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...