rlang::sym in anonymous functions

无人久伴 提交于 2019-11-30 22:22:40

问题


I recently notices that rlang::sym doesn't seem to work in anonymous functions and I don't understand why. Here an example, it's pretty clumsy and ugly but I think it illustrates the point

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")

This defines some toy data and a (horrible) function that essentially renames the columns based on column names. Now I can do the same thing for different combinations of a and b:

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

However, if I try to do the exact same thing with an anonymous function it doesn't work:

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

This fails with object 'a' not found even though the functions are exactly the same just here it is anonymous. Can anyone explain why?


回答1:


The issue is not anonymous functions, but the operator precedence of !!. The help page for !! states that

The !! operator unquotes its argument. It gets evaluated immediately in the surrounding context.

This implies that when you write a complex NSE expression, such as select inside mutate, the unquoting will take place in the environment of the expression as a whole. As pointed out by @lionel, the unquoting then takes precedence over other things, such as creation of anonymous function environments.

In your case the !! unquoting is done with respect to the outer mutate(), which then attempts to find column x1 inside d, not data. There are two possible solutions:

1) Pull the expression involving !! into a standalone function (as you've done in your question):

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

2) Replace !! with eval to delay expression evaluation:

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


来源:https://stackoverflow.com/questions/51902438/rlangsym-in-anonymous-functions

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!