接受裸(来自 rlang)或字符串作为函数输入
Accept both bare (from rlang) or string as a function input
我正在编辑包中的现有函数。目前,该函数接受数据框中的列名作为字符串。我正在更新函数以接受字符串名称或裸名。但我 运行 遇到了一些问题。
我想采用的一般方法是将 bare 转换为字符串,因此不需要更新函数的其余部分。如果用户传递的是字符串列名,那么我就不需要修改输入了。
下面的代码将纯输入转换为字符串,但我不知道如何有条件地转换为字符串或保持字符串不变。
test_fun <- function(by) {
# convert to enquo
by1 <- rlang::enquo(by)
# convert enquo to string
by2 <- rlang::quo_text(by1)
by2
}
# converts to string
test_fun(varname)
# not sure how to pass this unmodified
test_fun("varname")
我同意@Konrad 的评论,但您可以使用 base R 轻松做到这一点:
test_fun <- function(by) {
res <- substitute(by)
if (is.character(res)) return(res)
if (is.name(res)) return(deparse(res))
stop("unsupported input")
}
test_fun(varname)
#[1] "varname"
test_fun("varname")
#[1] "varname"
test_fun(y ~ x)
#Error in test_fun(y ~ x) : unsupported input
如前所述,我强烈建议不要接受多种类型,如果这会造成歧义(这里的确如此)。
即是说,以下是这样做的:
test_fun = function (by) {
by = substitute(by)
if (is.name(by)) {
as.character(by)
} else if (is.character(by)) {
by
} else {
stop('Unexpected type')
}
}
在这种情况下,使用 rlang 不会简化代码。
rlang::ensym()
的存在几乎就是为了这个目的,除了它的输出是一个名称而不是字符串,所以你需要转换它。
test_fun <- function(by) {
as.character(rlang::ensym(by))
}
test_fun(varname)
#> [1] "varname"
test_fun("varname")
#> [1] "varname"
由 reprex package (v0.2.1)
于 2019-08-08 创建
我觉得这样做不一定不好,foo <- "bar"
和"foo" <- "bar"
是等价的,"head"(iris)
和head(iris)
是等价的,ensym()
使 select(iris, "Species")
和 select(iris, Species)
等值变得容易。它对于交互式使用来说很方便,如果你希望你的功能与 dplyr::select()
甚至 base::library()
等一致,那么不支持此功能确实更令人惊讶。
只需确保它在您的用例中有意义,否则确实会造成混淆。
如果你想要弃用警告,你可以使用:
test_fun <- function(by) {
if(is.character(rlang::enexpr(by)))
warning("literal string input is deprecated, please use raw variable names")
as.character(rlang::ensym(by))
}
test_fun(varname)
#> [1] "varname"
test_fun("varname")
#> Warning in test_fun("varname"): literal string input is deprecated, please use raw
#> variable names
#> [1] "varname"
由 reprex package (v0.2.1)
于 2019-08-08 创建
我正在编辑包中的现有函数。目前,该函数接受数据框中的列名作为字符串。我正在更新函数以接受字符串名称或裸名。但我 运行 遇到了一些问题。
我想采用的一般方法是将 bare 转换为字符串,因此不需要更新函数的其余部分。如果用户传递的是字符串列名,那么我就不需要修改输入了。
下面的代码将纯输入转换为字符串,但我不知道如何有条件地转换为字符串或保持字符串不变。
test_fun <- function(by) {
# convert to enquo
by1 <- rlang::enquo(by)
# convert enquo to string
by2 <- rlang::quo_text(by1)
by2
}
# converts to string
test_fun(varname)
# not sure how to pass this unmodified
test_fun("varname")
我同意@Konrad 的评论,但您可以使用 base R 轻松做到这一点:
test_fun <- function(by) {
res <- substitute(by)
if (is.character(res)) return(res)
if (is.name(res)) return(deparse(res))
stop("unsupported input")
}
test_fun(varname)
#[1] "varname"
test_fun("varname")
#[1] "varname"
test_fun(y ~ x)
#Error in test_fun(y ~ x) : unsupported input
如前所述,我强烈建议不要接受多种类型,如果这会造成歧义(这里的确如此)。
即是说,以下是这样做的:
test_fun = function (by) {
by = substitute(by)
if (is.name(by)) {
as.character(by)
} else if (is.character(by)) {
by
} else {
stop('Unexpected type')
}
}
在这种情况下,使用 rlang 不会简化代码。
rlang::ensym()
的存在几乎就是为了这个目的,除了它的输出是一个名称而不是字符串,所以你需要转换它。
test_fun <- function(by) {
as.character(rlang::ensym(by))
}
test_fun(varname)
#> [1] "varname"
test_fun("varname")
#> [1] "varname"
由 reprex package (v0.2.1)
于 2019-08-08 创建我觉得这样做不一定不好,foo <- "bar"
和"foo" <- "bar"
是等价的,"head"(iris)
和head(iris)
是等价的,ensym()
使 select(iris, "Species")
和 select(iris, Species)
等值变得容易。它对于交互式使用来说很方便,如果你希望你的功能与 dplyr::select()
甚至 base::library()
等一致,那么不支持此功能确实更令人惊讶。
只需确保它在您的用例中有意义,否则确实会造成混淆。
如果你想要弃用警告,你可以使用:
test_fun <- function(by) {
if(is.character(rlang::enexpr(by)))
warning("literal string input is deprecated, please use raw variable names")
as.character(rlang::ensym(by))
}
test_fun(varname)
#> [1] "varname"
test_fun("varname")
#> Warning in test_fun("varname"): literal string input is deprecated, please use raw
#> variable names
#> [1] "varname"
由 reprex package (v0.2.1)
于 2019-08-08 创建