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

#r #rlang #quosure

#r #rlang #вопрос

Вопрос:

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

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

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

 library(tidyverse)
library(lifecycle)
library(rlang)
my_fun = function(df, cols, .vars = deprecated()){
  if (quo_is_missing(enquo(cols)) amp;amp; !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
 

Создано 2021-01-28 пакетом reprex (версия 0.3.0)

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

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

Ответ №1:

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

 my_fun = function(df, cols, .vars = deprecated()){
  if (quo_is_missing(enquo(cols)) amp;amp; !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)) amp;amp; !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}})
}
 

Комментарии:

1. Действительно, это решение, которое я рассматривал. К сожалению, я на самом деле не использую dplyr::select() , а внутреннюю функцию, которая ожидает запроса. Для изменения внутренней функции также потребуется некоторый значимый рефакторинг, поэтому я бы предпочел этого не делать.

2. Извините, это вышло неправильно, я имел в виду, что моя внутренняя функция ожидает имя, а не запрос. Точно так dplyr::select же.

3. Извините, последняя строка действительно должна быть select(df, {{cols}}) такой, чтобы она имитировала работу моей функции. Ваш ответ очень хорош, но для этого потребуется слишком много рефакторинга. Если другого решения нет, я приму его.

4. К сожалению, {{ это слишком ограничительно для того, что вы пытаетесь сделать. Он фиксирует выражение, предоставленное cols и немедленно использует его. Поскольку между двумя действиями нет промежутка, нет места, где вы можете изменить выражение. Единственный способ изменить то, что захватывается {{ , — это изменить способ my_fun вызова, возможно, с помощью оболочки.

5. Конечно, такой оболочкой может быть сама функция 😉 Пожалуйста, посмотрите мою правку.