测试将enquo()用于NULL参数的函数

时间:2018-01-29 15:42:42

标签: r dplyr

我有一个创建数据框的函数,但更改了进程中的名称。我试图用dplyr quosures处理空列名。我的测试套件如下所示:

dataframe <- data_frame(
  a = 1:5,
  b = 6:10
)

my_fun <- function(df, col_name, new_var_name = NULL) {
  target <- enquo(col_name)

  c <- df %>% pull(!!target) * 3 # here may be more complex calculations

  # handling NULL name
  if (is.null(new_var_name)) {
    new_name <- quo(default_name)
  } else{
    new_name <- enquo(new_name)
  }

  data_frame(
    abc = df %>% pull(!!target),
    !!quo_name(new_name) := c
  )
}

如果我这样称呼我的功能:

my_fun(dataframe, a)

我按预期获得默认名称:

# A tibble: 5 x 2
    abc default_name
  <int>        <dbl>
1     1            3
2     2            6
3     3            9
4     4           12
5     5           15

如果我试图传递名字,我会收到错误:

my_fun(dataframe, a, NEW_NAME)
Error in my_fun(dataframe, a, NEW_NAME) : object 'NEW_NAME' not found

我哪里错了?

2 个答案:

答案 0 :(得分:6)

这个问题与quoenquo返回不同的东西并不真正有关,它实际上是关于在你真正想要之前评估对象。如果您使用browser()逐步执行函数,则会在if (is.null(new_var_name))语句中看到错误。

执行is.null(new_var_name)时,您正在评估作为new_var_name传递的变量,因此enquo为时已晚。那是因为is.null需要查看变量的值而不仅仅是变量名本身。

不评估传递给函数的参数但检查它是否存在missing()的函数。

my_fun <- function(df, col_name, new_var_name=NULL) {
  target <- enquo(col_name)

  c <- df %>% pull(!!target) * 3 # here may be more complex calculations

  # handling NULL name
  if (missing(new_var_name)) {
    new_name <- "default_name"
  } else{
    new_name <- quo_name(enquo(new_var_name))
  }

  data_frame(
    abc = df %>% pull(!!target),
    !!new_name := c
  )
}

然后你可以运行这两个

my_fun(dataframe, a)
my_fun(dataframe, a, NEW_NAME)

答案 1 :(得分:1)

MrFlick 概述的方法不适用于嵌套函数调用。我们可以改用 rlang::quo_is_null

来自 rlang::quo_is_null 的文档:“当缺少的参数被捕获为 quosure 时,无论是通过 enquo() 还是 quos(),它们都会作为空的 quosure 返回”。所以当我们用空的quosures嵌套函数调用时,内部函数中对missing的调用最终会检查一个空的quosures是否为NULL,并且总是返回FALSE,因为它是NULL 的 quosure 而不是 quosure 本身。

我将以下详细函数放在一起以显示正在发生的事情:

library(dplyr)
library(rlang)

f1 <- function(var = NULL) {
    
    print(paste("substitute shows:", paste(substitute(var), collapse = " ")))
    print(paste("missing returns:", missing(var)))
    
    enquo_var <- enquo(var)
    print(paste("after enquo:", quo_get_expr(enquo_var)))
    print(paste("quo_is_null returns:", rlang::quo_is_null(enquo_var)))
    
    rlang::quo_is_null(enquo_var)
}

f2 <- function(var = NULL) {
    
    f1({{var}})
}

f1(Sepal.Length) 
f1() 

f2(Sepal.Length) 
f2() # this is where `missing` fails. 

注意:我欢迎对此解释进行更正或补充。非常感谢 mrflick、Lionel Henry 和 Hugh。 See here 相关问题。