#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. Конечно, такой оболочкой может быть сама функция 😉 Пожалуйста, посмотрите мою правку.