1

我有一个大致遵循这种结构的函数:

TestFunc <- function(dat, point) {
    if (!(point %in% c("NW", "SE", "SW"))) 
        stop("point must be one of 'SW', 'SE', 'NW'")

    point <- enquo(point)

    return(dat %>% filter(point == !!point))

问题是当我包含值检查时出现以下错误:

Error in (function (x, strict = TRUE)  : 
  the argument has already been evaluated

当我删除检查时,错误消失了。我怎样才能同时保留两者?

4

3 回答 3

3

关于 quosure 框架要记住的一点是,它是一段非常聪明、复杂的代码,可以撤销另一段非常聪明、复杂的代码,让您回到起点。您想要的东西可以通过一种非常简单的方式实现,而无需前往 NSE 并再次返回。

TestFunc <- function(dat, point)
{
    if(!(point %in% c("NW", "SE", "SW")))
        stop("point must be one of 'SW', 'SE', 'NW'")
    dat[dat$point == point, ]
}

match.arg(正如@Frank 在评论中所建议的那样,this 和 using 之间的区别在于,match.arg如果没有提供输入,它将使用第一个值作为默认值,而这将失败。)

如果您想调用其他 dplyr/tidyverse 动词,只需在过滤行后执行此操作。

于 2017-09-20T09:10:13.830 回答
1

由于与 R 如何优化代码有关的技术原因,您只能捕获尚未评估的参数。

因此,您首先必须捕获,enquo()然后继续检查该值。但是,如果您必须混合引用和基于值的代码,则通常表明存在设计问题。

正如 Hong 建议的那样,在您的情况下,您似乎可以直接取消引用该值而不捕获它。取消引用将确保找到正确的值(因为您为该变量提供了与数据框中的列相同的名称)。

于 2017-11-14T15:12:58.080 回答
0

评估point以便filter可以区分参数和数据列之间的区别

aosmith有一个好主意,所以我把它放在答案的形式,连同一个可重复的例子:

f <- function(dat, point) {
  if (!(point %in% c("NW", "SE", "SW")))
    stop("point must be one of 'SW', 'SE', 'NW'")

  filter(dat, point == !!point)
}

tbl <- tibble(point = c('SE', 'NW'))
f(tbl, 'SE')
f(tbl, 'XX')

如果您point作为字符串传入,则只需区分参数point(在本例中为 = "SE")和列dat$point(或tbl$point,取决于您是在函数内部还是外部)。来自dplyr 编程文档

在 dplyr (以及一般的 tidyeval 中)你使用 !! 说您想取消引用输入以便对其进行评估,而不是引用。

您想评估参数point以便将“SE”传递给filter,这样filter可以区分列point和参数SE

(我也尝试过使用filter(dat, .data$point == point),但 RHSpoint仍然指的是列,而不是f参数。)

我希望这个例子对你的真实代码有所帮助。

于 2017-11-16T17:40:55.263 回答