问题
I editing an existing function in a package. Currently, the function accepts a column name in a data frame as a string. I am updating the function to accept either a string name or a bare name. But I am running into some issues.
The general approach I'd like to take is to convert a bare to a string, so the rest of the function does not need to be updated. If the user passes a string column name, then I don't need to modify the input.
The code below converts a bare input to a string, but I can't figure out how to conditionally convert to a string or leave the string unmodified.
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")
回答1:
As noted, I strongly recommend against the practice of accepting multiple types if this creates ambiguity (and it does, here).
That said, the following does it:
test_fun = function (by) {
by = substitute(by)
if (is.name(by)) {
as.character(by)
} else if (is.character(by)) {
by
} else {
stop('Unexpected type')
}
}
Using rlang, in this case, doesn’t simplify the code.
回答2:
rlang::ensym()
exists pretty much for this purpose, except its output is a name not a string, so you need to convert it.
test_fun <- function(by) {
as.character(rlang::ensym(by))
}
test_fun(varname)
#> [1] "varname"
test_fun("varname")
#> [1] "varname"
Created on 2019-08-08 by the reprex package (v0.2.1)
I don't think it's necessarily bad to do so, foo <- "bar"
and "foo" <- "bar"
are equivalent, "head"(iris)
and head(iris)
are equivalent, ensym()
makes it easy to have things like select(iris, "Species")
and select(iris, Species)
be equivalent. It's handy for interactive use, and if you want your function to be consistent with dplyr::select()
, or even base::library()
etc it would indeed be more surprising NOT to support this feature.
Just make sure that it makes sense in your use case as it could otherwise indeed be confusing.
If you want a deprecation warning you can use :
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"
Created on 2019-08-08 by the reprex package (v0.2.1)
回答3:
I agree with @Konrad's comment but you can easily do this with 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
来源:https://stackoverflow.com/questions/57376099/accept-both-bare-from-rlang-or-string-as-a-function-input