用于编写函数的rlang运算符解释

3

最近我发布了两个与函数编写相关的问题(12)。我得到了每个问题有用的答案,从而得到了以下两个函数:

second_table <- function(dat, variable1, variable2){
 dat %>% 
  tabyl({{variable1}}, {{variable2}}, show_na = FALSE) %>% 
  adorn_percentages("row") %>% 
  adorn_pct_formatting(digits = 1) %>% 
   adorn_ns() 

 }

并且

second_table2 = function(dat, variable1, variable2){
  variable1 <- sym(variable1)
  
  dat %>% 
    tabyl(!!variable1, {{variable2}}, show_na = FALSE) %>% 
    adorn_percentages("row") %>% 
    adorn_pct_formatting(digits = 1) %>% 
    adorn_ns() 
  
}

这些函数可以正常工作,但在查看了可用文档并编写了一些额外的函数后,我仍然对r语言rlang包中的{{}}运算符和!! + sym()之间的区别感到困惑。我不喜欢使用我完全不理解的代码,并且肯定将来会进一步使用这些rlang运算符,因此非常希望能够以简单易懂的方式解释这些运算符之间的区别。


1
似乎有很多现有的指南,例如:https://www.tidyverse.org/blog/2019/06/rlang-0-4-0/,https://rlang.r-lib.org/reference/quasiquotation.html和https://dplyr.tidyverse.org/articles/programming.html。你能把你的问题更具体化吗?仅仅复制现有的解释在这里似乎没有用处。 - MrFlick
{{ 取代了 !!,更加简单易懂,请参考上述链接。 - Waldi
谢谢。我已经从上面链接的指南中了解到了要点,但仍然对特定用例感到困惑,比如在我的初始问题中的示例。我最感兴趣的是确定为什么您希望在一个函数中同时使用{{}}和!!与sym()结合使用,如果{{}}旨在替换!!。 - Emily Halford
1
有时候你需要直接访问 sym 对象,而有时候则不需要。在这个例子中,因为你直接使用变量,所以没有使用 sym()!! 的必要。但是如果你想要改变或修改变量,那么你就需要先操作 sym。另一个常见使用 sym() + !! 的原因是通过 as_label() 也将变量名作为字符串获取。使用 {{}} 是做不到这一点的。 - MrFlick
那很有道理,谢谢! - Emily Halford
1个回答

5

R有一个特殊的功能叫做非标准评估(NSE),其中表达式被原样使用而不被评估。大多数人第一次遇到NSE是在加载包时:

a <- "rlang"

print(a)         # Standard evaluation - the expression a is evaluated to its value
# [1] "rlang"

library(a)       # Non-standard evaluation - the expression a is used as-is
# Error in library(a) : there is no package called ‘a’

rlang通过提供三个主要函数来捕获未评估的符号和表达式,从而实现了复杂的非标准评估:

  • sym("x")捕获一个符号(例如变量名、列名等)。旧版本允许使用sym(x),但我认为最新版本的rlang强制输入必须是字符串。

  • expr(a + b)捕获任意表达式

  • quo(a + b)捕获任意表达式和定义这些表达式的环境。

expr表达式和quo表达式之间的区别在于前者的求值是在当前环境中进行的,而后者始终在捕获表达式的环境中进行求值:

f <- function(e) {a <- 2; b <- 3; eval_tidy(e)}
a <- 5; b <- 10

f(expr(a+b))   # Evaluated inside f
# [1] 5

f(quo(a+b))    # Evaluated in the environment where it is captured
# [1] 15

这三个动词都有对应的en版本:ensymenexprenquo。它们用于从函数内部获取传递给函数的符号和表达式。当您想要消除函数用户自己使用sym等时,这非常有用:

f <- function(x) {enexpr(x)}      # Expression captured within a function
f(a+b)

# This has exact equivalence to

f <- function(x) {x}
f(expr(a+b))                      # The user has to do the capture themselves

在所有情况下,运算符 !! 都会评估符号和表达式。 将其视为类似于 eval()的东西,因为 !! 会强制立即评估,这优先于其他所有内容。 除此之外,这还可以用于更复杂的表达式的迭代构建:
a <- expr(b + 2)
expr(d * !!a)      # a is evaluated immediately
# d * (b + 2)

expr(d * eval(a))  # evaluation of a is delayed
# d * eval(a)

说了这么多,{{x}}!!enquo(x)的速记表示。


那么我可以使用这个来捕获 <- 操作符左侧的符号。也就是说,例如我能否从 x <- 5 中得到 "x",并从 y <- 10 中得到 "y"? - Eden
最好使用AST完成:https://dev59.com/mLnoa4cB1Zd3GeqPJxQT - Artem Sokolov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接