接受裸(来自 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 创建