【问题标题】:rlang::sym in anonymous functionsrlang::sym 在匿名函数中
【发布时间】:2019-01-24 21:54:02
【问题描述】:

我最近注意到rlang::sym 似乎不适用于匿名函数,我不明白为什么。这是一个例子,它非常笨拙和丑陋,但我认为它说明了这一点

require(tidyverse)
data <- tibble(x1 = letters[1:3],
               x2 = letters[4:6],
               val = 1:3)

get_it <- function(a, b){
    data %>%
        mutate(y1 = !!rlang::sym(a)) %>%
        mutate(y2 = !!rlang::sym(b)) %>%
        select(y1, y2, val)
}
get_it("x1", "x2")

这定义了一些玩具数据和一个(可怕的)函数,该函数本质上是根据列名重命名列。现在我可以对 a 和 b 的不同组合做同样的事情:

d <- tibble(x = c("x1", "x2"),
            y = c("x2", "x1"))
d %>% mutate(tmp = map2(x, y, get_it))

但是,如果我尝试对匿名函数执行完全相同的操作,则它不起作用:

d %>% mutate(tmp = map2(x, y, function(a, b){
data %>%
    mutate(y1 = !!rlang::sym(a)) %>%
    mutate(y2 = !!rlang::sym(b)) %>%
    select(y1, y2, val)
}))

即使功能完全相同,object 'a' not found 也会失败,只是这里是匿名的。谁能解释一下为什么?

【问题讨论】:

  • 嗯,真正的益智游戏。我认为这一定与定义函数的环境有关,但无法区分...
  • 这可能不是错误,但我会在 Git 上将其报告为问题。
  • 如果我们消除这里实际上不需要的 rlang,那么它就可以工作:function(a, b) data %&gt;% mutate(y1 = .[[a]], y2 = .[[b]]) %&gt;% select(y1, y2, val) 所以看起来匿名函数可以工作,但其中不包含 rlang。
  • 取消引用不是函数调用:它总是在第一个最外面的引用函数处生效。这就是为什么您必须小心使用匿名函数的原因。取消引用会立即发生,而匿名函数表示稍后创建的范围,因此存在时间问题。
  • 这是我们决定弃用 UQ()UQS() 的原因之一,尽管它们的语义非常不同,但它们看起来太像函数调用了。

标签: r rlang nse tidyeval


【解决方案1】:

问题不是匿名函数,而是!! 的运算符优先级。 !! 的帮助页面指出

!!运算符取消引用其参数。它会在周围环境中立即进行评估。

这意味着当你编写一个复杂的 NSE 表达式时,例如 selectmutate 中,取消引用将在整个表达式的环境中发生。正如@lionel 所指出的,取消引用优先于其他事情,例如创建匿名函数环境。

在您的情况下,!! 取消引用是针对外部mutate() 完成的,然后它会尝试在d 中查找列x1,而不是data。有两种可能的解决方案:

1) 将涉及!! 的表达式拉入一个独立的函数中(正如您在问题中所做的那样):

res1 <- d %>% mutate(tmp = map2(x, y, get_it))

2) 将 !! 替换为 eval 以延迟表达式评估:

res2 <- d %>% mutate(tmp = map2(x, y, function(a, b){
  data %>%
    mutate(y1 = eval(rlang::sym(a))) %>%
    mutate(y2 = eval(rlang::sym(b))) %>%
    select(y1, y2, val)
}))

identical(res1, res2)       #TRUE

【讨论】:

  • 这总体上是一个很好的答案,但问题不在于嵌套的变异。这是匿名函数中的取消引用,正如您在 !!i 示例中所展示的那样。
  • 虽然我猜使用嵌套的变异你可能会产生类似的时间问题。
  • 感谢您的澄清,@lionel。听到开发人员的来信总是很棒。我认为这是一个嵌套问题的原因是因为我的最后一个示例在匿名函数中有 !! 并且工作正常。但你是对的;它归结为 NSE 表达式中的运算符优先级,而不是嵌套。我已经修改了答案中的语言,使其更加精确。 (P.S. 你的 rlang 包的忠实粉丝!)
  • 基于my related issue,您可以在答案中添加使用evaleval_tidy 而不是!! 解决优先级问题,尤其是在嵌套mutate 的上下文中出现s.
  • @TimTeaFan:完成。感谢您的建议。
猜你喜欢
  • 2018-08-18
  • 2019-02-04
  • 2019-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-30
  • 2014-09-11
  • 1970-01-01
相关资源
最近更新 更多