Истинная разница между rlang :: enexpr и base :: substitute - PullRequest
4 голосов
/ 23 января 2020

Этот вопрос относится к тому, который выложил здесь .

Короче говоря, я ищу, почему base::substitute и rlang::enexpr ведут себя по-разному ниже.

#works
f1 <- function(x,y){ 
  do.call("methods", list(substitute(x::y)))
}
f1(broom,tidy)
#does not work
#Error: `arg` must be a symbol
f2 <- function(x,y){
  do.call('methods',list(rlang::enexpr(x::y)))
}
f2(broom,tidy)

Более длинная версия. В расширенной книге R глава 19 вы можете видеть, что таблицы 19.1 и 19.2 предполагают, что enexpr и substitute должны служить одной и той же цели при вызове функции (я могу ошибаться и был бы счастлив с объяснение почему я не прав).

Я решил проверить это и увидел, что в f1 возвращает результаты, но f2 возвращает ошибку.

Интересно, если вы используете do.call('methods',list(rlang::expr(broom::tidy))), это работает. Мне это интересно, потому что rlang::expr просто звонит rlang::enexpr.

В приведенном выше вопросе MrFlick разместил эту функцию как чистое решение Rlang

#also works 
#function from MrFlick in the posted link
f3 <- function(x,y){
x <- rlang::ensym(x)
y <- rlang::ensym(y)
rlang::eval_tidy(rlang::quo(methods(`::`(!!x, !!y))))}

f3(broom,tidy) 

Это кажется немного сложнее, чем я ожидал.

Было бы полезно узнать, почему f1 и f2 не эквивалентны или как заставить f2 работать с enexpr.

1 Ответ

1 голос
/ 23 января 2020

rlang::enexpr() и base::substitute() не совсем эквивалентны по своему интерфейсу. enexpr() ожидает одно имя переменной, которое ссылается на один из входных аргументов функции, тогда как substitute() может работать с произвольными выражениями. Для этого требуется дополнительное арифметическое выражение c - с помощью rlang::expr() и оператора unquote !! - для помещения результата enexpr() в более сложное выражение:

g  <- function(x) substitute(x+5)
h  <- function(x) rlang::enexpr(x+5)
h2 <- function(x) rlang::expr( !!rlang::enexpr(x) + 5 )

g(a)   # a + 5
h(a)   # Error: `arg` must be a symbol
h2(a)  # a + 5

. f2 работа, вам нужно применить rlang::enexpr() к каждому аргументу отдельно, затем использовать выражение arithmeti c для составления общего выражения x::y:

f2 <- function(x,y){
   ee <- rlang::expr( `::`(!!rlang::enexpr(x), !!rlang::enexpr(y)) )
   do.call('methods',list(ee))
}
f2(broom,tidy)

Обратите внимание, что мы должны использовать :: в префиксной записи, потому что наличие !! и :: рядом друг с другом является проблемой для синтаксического анализатора. Другими словами, выражения типа !!a :: !!b приводят к ошибкам синтаксического анализа.

Альтернатива состоит в том, чтобы позволить пользователю составить выражение самостоятельно. Таким образом, вы можете выбрать один rlang::enexpr():

f3 <- function(x)
  do.call('methods',list(rlang::enexpr(x)))
f3( broom::tidy )

Side Note:

Несмотря на то, что expr() вызывает enexpr(), этот вызов по отношению к сфере действия expr(). Рассмотрим,

f1 <- function(x)  rlang::enexpr(x)

f2 <- function(x) {
  g <- function(y) rlang::enexpr(y)
  g(x)
}

Два не эквивалентны, потому что второй enexpr() ограничен внутренней функцией g(), а не внешней f2().

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