首页 > 解决方案 > Expand call stored in a variable before passing to function

问题描述

I have a call stored in a variable. If relevant, the call originates from a one-sided formula:

f <- ~ a + b
rhs <- rlang::f_rhs(f)
rhs
#> a + b

Created on 2020-03-19 by the reprex package (v0.3.0)

Now, the formula might contain bquote-style escaping .(), and I want to get all the escaped names.

Looking at bquote's source code, I created this function:

escapedNames <- function (expr, where = parent.frame()) 
{
    unquote <- function(e) {
        if (is.pairlist(e)) {
            NULL
        } else if (length(e) <= 1L) {
             NULL
        } else if (e[[1L]] == as.name(".")) {
            deparse(e[[2L]])
        } else {
            x <- sapply(e, unquote)
            unlist(x)
        }
    }
    unquote(substitute(expr))
}

escapedNames(a + .(b) + c)
#> [1] "b"

As you can see, it works just fine for hard-coded expressions. However, I can't figure out how to pass calls stored in variables.

f <- ~ a + .(b)
rhs <- rlang::f_rhs(f)
escapedNames(rhs)
#> NULL

This makes perfect sense, of course, since escapedNames treats whatever it is given as the call I want to analyze, so it doesn't know to expand rhs before looking for names.

So, how can I solve this?

标签: rfunction

解决方案


发布赏金后,我立即想出了一个方法,但请告诉我一个更好的方法!

我们可以在评估之前使用evalandrlang::expr来强制扩展公式变量escapedNames

escapedNames <- function (expr) 
{
    unquote <- function(e) {
        if (is.pairlist(e) || length(e) <= 1L) NULL
        else if (e[[1L]] == as.name("."))      deparse(e[[2L]])
        else                                   unlist(sapply(e, unquote))
    }
    unquote(substitute(expr))
}

formula <- ~ a + .(b) + c + .(d)
rhs <- rlang::f_rhs(formula)

eval(rlang::expr(escapedNames(!!rhs)))
#> [1] "b" "d"

reprex 包(v0.3.0)于 2020-03-23 创建


推荐阅读