3

我编辑包中的现有功能。目前,该函数接受数据框中的列名作为字符串。我正在更新函数以接受字符串名称或裸名称。但我遇到了一些问题。

我想采用的一般方法是将裸字符转换为字符串,因此不需要更新函数的其余部分。如果用户传递一个字符串列名,那么我不需要修改输入。

下面的代码将裸输入转换为字符串,但我不知道如何有条件地转换为字符串或不修改字符串。

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")
4

3 回答 3

2

如前所述,如果这会造成歧义,我强烈建议不要接受多种类型的做法(这里确实如此)。

也就是说,以下是这样做的:

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 不会简化代码。

于 2019-08-06T12:36:59.527 回答
2

rlang::ensym()几乎为此目的而存在,除了它的输出是名称而不是字符串,因此您需要对其进行转换。

test_fun <- function(by) {
  as.character(rlang::ensym(by))
}

test_fun(varname)
#> [1] "varname"

test_fun("varname")
#> [1] "varname"

reprex 包(v0.2.1)于 2019 年 8 月 8 日创建

我认为这样做不一定是坏事,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 包(v0.2.1)于 2019 年 8 月 8 日创建

于 2019-08-08T15:13:22.807 回答
1

我同意@Konrad 的评论,但您可以使用基本 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
于 2019-08-06T12:36:40.120 回答