Хорошая философия rlang
заключается в том, что вы можете контролировать, когда хотите, чтобы значения оценивались с помощью операторов !!
и {{}}
. Кажется, вы хотите создать функцию, которая принимает строки, символы и (возможно, вычисляемые) выражения в одном и том же параметре. Использовать символы или пустые строки на самом деле легко с ensym
, но также требуется разрешить код, подобный colnames(mtcars)[9]
, который должен быть evaulated , прежде чем возвращать строку - проблема. Это может быть довольно запутанным. Например, какое поведение вы ожидаете, когда запускаете следующее?
am <- 'disp'
cyl <- 'gear'
foo(mtcars, am, cyl)
Вы можете написать вспомогательную функцию, если хотите предположить, что все «вызовы» должны оцениваться, а символы и литералы - нет. Вот «более чистая» функция
clean_quo <- function(x) {
if (rlang::quo_is_call(x)) {
x <- rlang::eval_tidy(x)
} else if (!rlang::quo_is_symbolic(x)) {
x <- rlang::quo_get_expr(x)
}
if (is.character(x)) x <- rlang::sym(x)
if (!rlang::is_quosure(x)) x <- rlang::new_quosure(x)
x
}
, и вы можете использовать ее в своей функции с
foo <- function(data, x, y) {
x <- clean_quo(rlang::enquo(x))
y <- clean_quo(rlang::enquo(y))
# function without formula
print(table(data %>% dplyr::pull(!!x), data %>% dplyr::pull(!!y)))
# function with formula
print(
broom::tidy(stats::t.test(
formula = rlang::new_formula(rlang::quo_get_expr(y), rlang::quo_get_expr(x)),
data = data
))
)
}
Это позволит всем этим возвращать одинаковые значения
foo(mtcars, am, cyl)
foo(mtcars, "am", "cyl")
foo(mtcars, colnames(mtcars)[9], colnames(mtcars)[2])
Но вы, вероятно, просто задерживаете возможные другие проблемы. Я не рекомендовал бы чрезмерно интерпретировать намерения пользователей с таким кодом. Вот почему лучше явно позволить им убежать самим. Возможно, предоставьте две разные версии функции, которые можно использовать с параметрами, которые требуют оценки, и те, которые не требуют.