R & quosures - How to get names of symbols contained in a vector passed as function argument?

醉酒当歌 提交于 2021-02-09 08:56:28

问题


I want to write an R function arg2str that returns the names (that is a vector of strings) of the symbols that are fed as arguments.

For the most simple case, I only have one input symbol:

library ("rlang")

arg2str.v0 <- function (arg) rlang::quo_name (enquo (arg))
arg2str.v0 (a)
## [1] "a"

If I have multiple symbols, I can use the three-dots construct:

arg2str.v1 <- function (...) sapply (enquos (...), rlang::quo_name)
arg2str.v1 (a, b, c)
##             
## "a" "b" "c"

(Subsidiary question: why is the resulting vector of strings displayed with a preliminary line break instead of a preliminary [1] in this case?)

But I actually want to deal with vectors of symbols. Yet:

sym2str.v1 (c(a, b, c))
##
## "c(a, b, c)"

How do I tune my function to do so?


My first intuition was to first enquote the symbols contained in the argument vector by using sapply (instead of enquoting the argument vector itself), then to apply rlang::quo_name to the resulting vector of quosures. But it seems that the symbols in the argument vector are evaluated within sapply before enquote is called on each of them:

arg2str.v2 <- function (args) {
    enquo_args <- sapply (args, enquo)
    lapply (enquo_args, rlang::quo_name)
}
arg2str.v2 (c (a, b, c))
## Error in lapply(X = X, FUN = FUN, ...) : object 'a' not found

回答1:


Using rlang conventions, this should work:

return_args <- function(args){

    args_expr <- enexpr(args)

    if(length(args_expr) == 1) {
        args_vars <- as.list(args_expr)
    } else {
        args_vars <- as.list(args_expr)[-1]
    }

    sapply(args_vars, quo_name)
}


return_args(c(a, b, c))
[1] "a" "b" "c"

return_args(a)
[1] "a"




回答2:


I am not sure if there is a vectorised tidyeval operation for what you want. You can try

f <- function(v) {
    v <- rlang::quo_name(enquo(v))
    gsub('^c\\(|\\s|\\)$', '', v) %>% 
        strsplit(',') %>% 
        unlist 
}

f(c(a, b, c))
#[1] "a" "b" "c"

which will work with inputs of the form a, c(a) or c(a, b) but it's a bit hacky...

The answer to your side question (why arg2str.v1(a, b, c) does not print the "[1]" at the beginning of the line) is that it returns a named vector, whose names are empty strings (compare e.g. with the output of set_names(c('a', 'b', 'c'), c('', '', ''))).




回答3:


One possible hacky answer using substitute and deparse:

arg2str.v3 <- function (args) {
    strs <- sapply (substitute (args), deparse)
    if (length (strs) > 1) { strs <- strs [-1] }
    return (strs)
}
arg2str.v3 (c (a, b, c))
## [1] "a" "b" "c"

Note that it also preserves names if a named vector is provided as input:

arg2str.v3 (c (n1 = a, n2 = b, n3 = c))
##  n1  n2  n3 
## "a" "b" "c"


来源:https://stackoverflow.com/questions/52114227/r-quosures-how-to-get-names-of-symbols-contained-in-a-vector-passed-as-funct

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!