Как присвоить quosure другому объекту?

Я хочу изменить имя некоторого аргумента.

Следуя рекомендациям, я должен используйте lifecycle::deprecate_warn, а затем присвойте старое имя новому имени.

Однако в моей функции аргумент обычно используется с квазурами, поэтому атрибуция завершается с ошибкой:

library(tidyverse)
library(lifecycle)
library(rlang)
my_fun = function(df, cols, .vars = deprecated()){
  if (quo_is_missing(enquo(cols)) && !quo_is_missing(enquo(.vars))) {
    deprecate_warn("0.1.6", "my_fun(.vars=)", "my_fun(cols=)")
    cols <- .vars #error is thrown here
  }
  select(df, {{cols}})
}

my_fun(iris, cols=Sepal.Length) %>% head()
#>   Sepal.Length
#> 1          5.1
#> 2          4.9
#> 3          4.7
#> 4          4.6
#> 5          5.0
#> 6          5.4
my_fun(iris, .vars=Sepal.Length) %>% head()
#> Warning: The `.vars` argument of `my_fun()` is deprecated as of <NA> 0.1.6.
#> Please use the `cols` argument instead.
#> This warning is displayed once every 8 hours.
#> Call `lifecycle::last_warnings()` to see where this warning was generated.
#> Error in my_fun(iris, .vars = Sepal.Length): objet 'Sepal.Length' introuvable

Создано 28 января 2021 г. с помощью пакета reprex (v0.3.0)

Я вслепую пробовал разные вещи с enquo и другими, но ничего не получалось.

Как я могу привязать старое имя к новому имени?


person Dan Chaltiel    schedule 28.01.2021    source источник


Ответы (1)


Напомним, что {{ — это сокращение от !!enquo(). Поскольку вы хотите указать либо cols, либо .vars, в зависимости от того, какой из них отсутствует, я предлагаю сделать !! и enquo() отдельно:

my_fun = function(df, cols, .vars = deprecated()){
  if (quo_is_missing(enquo(cols)) && !quo_is_missing(enquo(.vars))) {
    deprecate_warn("0.1.6", "my_fun(.vars=)", "my_fun(cols=)")
    cols <- enquo(.vars)    # Quote .vars, if cols is missing
  }
  else cols <- enquo(cols)  # Quote cols, if cols is not missing

  select(df, !!cols)        # Unquote with !!, instead of {{, which is !!enquo()
}

my_fun(iris, cols=Sepal.Length) %>% head()    # Works
my_fun(iris, .vars=Sepal.Length) %>% head()   # Also works

Если вам абсолютно необходимо использовать {{, единственный способ изменить полученное выражение — это изменить способ вызова функции. Это можно сделать с помощью небольшой рекурсии (т. е. вызывая сам my_fun):

my_fun = function(df, cols, .vars = deprecated()){
  if (quo_is_missing(enquo(cols)) && !quo_is_missing(enquo(.vars))) {
    deprecate_warn("0.1.6", "my_fun(.vars=)", "my_fun(cols=)")
    return( my_fun(df, {{.vars}}) )   # .vars will be captured as cols
  }

  select(df, {{cols}})
}
person Artem Sokolov    schedule 28.01.2021
comment
Действительно, это решение, которое я рассматривал. К сожалению, на самом деле я использую не dplyr::select(), а внутреннюю функцию, которая ожидает quosure. Для изменения внутренней функции также потребуется некоторый значимый рефакторинг, поэтому я бы не стал этого делать. - person Dan Chaltiel; 28.01.2021
comment
Извините, не так получилось, я имел в виду, что моя внутренняя функция ожидает имя, а не quosure. Так же, как dplyr::select. - person Dan Chaltiel; 28.01.2021
comment
Извините, последняя строка действительно должна быть select(df, {{cols}}), чтобы она имитировала работу моей функции. Ваш ответ очень хорош, но он потребует слишком большого рефакторинга. Если нет другого решения, я соглашусь. - person Dan Chaltiel; 28.01.2021
comment
К сожалению, {{ слишком ограничивает то, что вы пытаетесь сделать. Он фиксирует выражение, предоставленное cols, и сразу же использует его. Поскольку между двумя действиями нет промежутка, нет места, где вы можете изменить выражение. Единственный способ изменить то, что захватывается {{, — это изменить способ вызова my_fun, возможно, с помощью оболочки. - person Artem Sokolov; 28.01.2021
comment
Конечно, такой оберткой может быть и сама функция ;) Пожалуйста, смотрите мое редактирование. - person Artem Sokolov; 28.01.2021