Снятие в кавычках переменной цикла в `rlang :: expr` - PullRequest
0 голосов
/ 27 августа 2018

Я столкнулся с неожиданным поведением при использовании оператора !! для итеративного построения выражений, включающих переменную цикла.

Создание выражения из самой переменной цикла, например,

X <- list()
for( i in 1:3 )
  X[[i]] <- rlang::expr( !!i )
str(X)
# List of 3
#  $ : int 1
#  $ : int 2
#  $ : int 3

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

Y <- list()
for( i in 1:3 )
    Y[[i]] <- rlang::expr( 1 + (!!i) )
str(Y)
# List of 3
#  $ : language 1 + 3L
#  $ : language 1 + 3L
#  $ : language 1 + 3L

, похоже, приводит к выражениям, которые содержат окончательное значение переменной цикла.Это почти как если бы выражения были захвачены как quote( 1 + (!!i) ) во время цикла, но снятие кавычек через !! происходит только после выполнения всего цикла, в отличие от каждой итерации.

Может кто-нибудь, пожалуйста, сбросьте немногосвет на это поведение?Это ошибка?Предполагаемая цель состоит в том, чтобы захватить безошибочные выражения 1+1, 1+2 и 1+3 с помощью цикла.Следовательно, ожидаемый результат Y будет

# List of 3
#  $ : language 1 + 1L
#  $ : language 1 + 2L
#  $ : language 1 + 3L

. Примечание: дополнительные скобки вокруг !!i предназначены для решения потенциальной проблемы приоритета оператора .

Использование rlang 0.2.1.

Ответы [ 3 ]

0 голосов
/ 27 августа 2018

Похоже, что for создает некую среду / фрейм, где оценка происходит в конце (для последнего i).Один из возможных способов справиться с этим - избегать for и использовать purrr:: или lapply.

Y <- purrr::map(1:3, ~ rlang::expr( 1 + (!! .) )) 
str(Y)
#> List of 3
#>  $ : language 1 + 1L
#>  $ : language 1 + 2L
#>  $ : language 1 + 3L

Y <- lapply(1:3, function(i) rlang::expr( 1 + (!! i) )) 
str(Y)
#> List of 3
#>  $ : language 1 + 1L
#>  $ : language 1 + 2L
#>  $ : language 1 + 3L

while работает в консоли, но не работает при использовании с reprex (не показано).).

Y <- list()
i <- 1L
while (i <= 3) {
    Y[[i]] <- rlang::expr( 1 + (!!i) )
    i <- i + 1L
}
str(Y)
#> List of 3
#>  $ : language 1 + 1L
#>  $ : language 1 + 2L
#>  $ : language 1 + 3L
0 голосов
/ 27 августа 2018

Не совсем уверен, так ли это, но я думаю, что это связано с ленивой оценкой.Основная идея состоит в том, что R не оценивает выражение, когда оно не используется.В вашем примере rlang::expr( !!i ) является самооценкой, потому что выражение, представляющее константу, является самой константой.Это приводит к тому, что на каждой итерации цикла for выполняется следующее:

X <- list()
for( i in 1:3 )
  X[[i]] <- rlang::expr( !!i )

Обратите внимание, что элементы X больше не являются выражениями, а являются целыми числами:

> str(X)
List of 3
 $ : int 1
 $ : int 2
 $ : int 3

identical(rlang::expr(1),1)
# [1] TRUE

Ваш второй примероднако имеет rlang::expr( 1 + (!!i) ), которое остается выражением на каждой итерации цикла for без вычисления.Ленивая оценка заставляет R оценивать только i только в конце цикла, который принимает последнее значение i.Способ устранения этой проблемы - force оценка i:

Y <- list()
for( i in 1:3 ){
  force(i)
  Y[[i]] <- rlang::expr( 1 + (!!i) )
}

> str(Y)
List of 3
 $ : language 1 + 1L
 $ : language 1 + 2L
 $ : language 1 + 3L

Обратите внимание, что ленивая оценка также влияла на такие функции, как lapply, как обсуждалось в этом вопросе: Объяснителенивая оценка , но с тех пор она была исправлена ​​в R 3.2.0.Функции высшего порядка, такие как lapply, теперь передают аргументы внутренней функции.Смотрите ответ @ jhin в том же вопросе.Вот почему решение @Mike Badescu lapply теперь работает.

0 голосов
/ 27 августа 2018

Я не уверен на 100%, что правильно вас понял.Возможно, вы после rlang::eval_tidy оценили выражение?

X <- list()
for( i in 1:3 )
    X[[i]] <- rlang::eval_tidy(rlang::expr(!!i + 1))
#[[1]]
#[1] 2
#
#[[2]]
#[1] 3
#
#[[3]]
#[1] 4
...