How to write an NES function that also takes chara

2019-07-13 14:05发布

I´m working on a R package that takes strings as function arguments. Now I want to use non-standard evaluation to allow for non-string input. Also, to keep the backwards compatibility, I´d like to keep the possibility for the functions to take strings.

Hadley gives an example with the subset function and suggests that every NES function should be accompanied by a standard evaluation function.

library(lazyeval)
# standard evaluation
subset2_ <- function(df, condition) {
    r <- lazy_eval(condition, df)
    r <- r & !is.na(r)
    df[r, , drop = FALSE]
} 

subset2_(mtcars, lazy(mpg > 31))


# NES can be written easily afterwards

subset2 <- function(df, condition) {
    subset2_(df, lazy(condition))
}

While the SE function now also takes quoted input,

subset2_(mtcars, "mpg > 31")

the NSE function throws an error:

subset2(mtcars, "mpg > 31")

But I want the user to have the same function (the NSE function) for both quoted as well as unquoted arguments.

Any ideas?

1条回答
\"骚年 ilove
2楼-- · 2019-07-13 14:30

The NSE function takes NSE input. That’s the point of this pattern, isn’t it?

subset2(mtcars, mpg > 31)

You can of course allow the NSE function to take character input as well but I’d advise against this — strongly. Don’t mix SE and NSE, there’s no advantage to be had, and it sows confusion (and potentially bugs, since you’re mixing domains).

That said, the following of course works:

subset2 <- function(df, condition) {
    if (is.character(substitute(condition)))
        subset2_(df, condition)
    else
        subset2_(df, lazy(condition))
}

If you want to allow NSE and SE in the same function for backwards compatibility reasons, I suggest phasing out the SE version in a future version, and adding a deprecation warning for now. To add the deprecation warning:

subset2 <- function(df, condition) {
    if (is.character(substitute(condition))) {
        msg = sprintf(paste0('Calling %s with a quoted expression is',
                             ' deprecated. Pass an unquoted expression',
                             ' instead, or use %s.'),
                      sQuote('subset2'), sQuote('subset2_'))
        .Deprecated(msg = msg)
        subset2_(df, condition)
    }
    else
        subset2_(df, lazy(condition))
}
查看更多
登录 后发表回答