使用 `!!` 而不附加 `rlang`

using `!!` without attaching `rlang`

我正在写一个包,我的一个函数生成了一个 ggplot。我只想导入 ggplot2 或 rlang(不依赖于它们)。经过一些尝试和错误后,我设法让它工作,但现在我不确定为什么它能工作。

所以我的问题是,为什么下面的代码可以在不使用 :: 直接访问 !! 的情况下工作?

arg1 <- "Species"
ggplot2::ggplot(iris, ggplot2::aes(x = Petal.Width, y = !!rlang::sym(arg1))) + 
    ggplot2::geom_bar(stat = "summary", fun = "max")

我的理解是,为了访问 !! 函数,我必须使用 :: 指定包,但是这个示例有效,所以我缺少什么?

之所以有效,是因为“rlang”/tidy 求值实际上并没有解析 !! 运算符,它甚至没有 define 这样的运算符——事实上这个运算符甚至 不存在! 它只是两个链式 ! 运算符,它们永远不会被评估,因为整洁的评估使用 non-standard evaluation。 “rlang”中的实际实现是在 C++ 中,修复 R 运算符优先级规则中的不匹配相当复杂,但功能子集的简化版本可能如下所示:

bang = as.name('!')

interpolate_bang_bang = function (expr, envir) {
    if (is.call(expr) && expr[[1L]] == bang) {
        if (is.call(expr[[2L]]) && expr[[2L]][[1L]] == bang) {
            eval(expr[[2L]][[2L]], envir = envir)
        } else {
            expr
        }
    } else {
        expr
    }
}

这会测试未评估的表达式是否恰好是 !! ‹something›,并将其替换为 ‹something› 的评估版本。真正的实现要复杂得多,因为它需要处理任意复杂的嵌套表达式(例如 1 + !!x),而且它还要做很多其他事情。但上面说明了基本事实:没有 !! 运算符。相反,“rlang”检查未评估的表达式是否包含两个立即嵌套的 ! 运算符调用。

因此,即使您愿意,也无法导入或附加 !! 运算符。

如果加载了 rlang 包(或者甚至加载了 ggplot2 包),那么使用 help("!!") 会打开一个帮助页面,将其命名为“注入运算符”,建议 Konrad Rudolph 的回答错误 或至少 out-of-sync 哈德利版本的 NSE 说法。对 Konrad 的回答的评论中给出的 link 更有帮助:https://adv-r.hadley.nz/quasiquotation.html#the-polite-fiction-of。 (Konrad 的错误将是一个非常不寻常的例子。)“注入运算符”,因为它在那里被称为,也在 `help("topic-inject-out-of-context").

中提到。

!! and !!! behave specially inside all quoting functions powered by rlang, where they behave like real operators with precedence equivalent to unary + and -.

在当前实例中,您也可以使用 "! ! " 获得相同的结果,我觉得这很奇怪,但可以用“polite-fiction”来解释 material.

我怀疑调用 ggplot2::ggplot 的简单行为会将一堆必需的包加载到可访问的命名空间集中,即使您试图通过使用“::”方法来阻止这种情况。所以 ”!!”尽管努力防止这种情况发生,但确实以 NSE 方式进行评估:

#In an entirely  base session with only required packages loaded ....
ggplot2::ggplot(iris, ggplot2::aes(x = Petal.Width))
#-----------------------------------
> sessionInfo()
R version 4.1.2 (2021-11-01)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.5 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/openblas/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/libopenblasp-r0.2.20.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8    LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
 [1] magrittr_2.0.2   tidyselect_1.1.2 munsell_0.5.0    colorspace_2.0-3 R6_2.5.1         rlang_1.0.2     
 [7] fansi_1.0.2      dplyr_1.0.8      tools_4.1.2      grid_4.1.2       gtable_0.3.0     utf8_1.2.2      
[13] cli_3.2.0        DBI_1.1.2        ellipsis_0.3.2   assertthat_0.2.1 digest_0.6.29    tibble_3.1.6    
[19] lifecycle_1.0.1  crayon_1.5.0     purrr_0.3.4      ggplot2_3.3.5    vctrs_0.3.8      glue_1.6.2      
[25] labeling_0.4.2   compiler_4.1.2   pillar_1.7.0     generics_0.1.2   scales_1.1.1     pkgconfig_2.0.3