我使用了%>%,即magrittr管道,就像它的文档中给出的那样,向this answer中的RHS提供了一个不带空括号的函数,得到的注释是the recommended convention is to supply empty parentheses to the RHS.

library(magrittr)

1:3 %>% sum    # The documentation calls this: Basic use

1:3 %>% sum()  # It's also possible to supply empty parentheses
1:3 |> sum()   # And It's similar to |> the base pipe

一个优点可能是其语法类似于基本管道|>.

但另一方面,%>%也可以像函数一样使用,并且这些函数通常不带括号.

`%>%`(1:3, sum)

sapply(list(1:3), sum)
`%=>%` <- sapply
list(1:3) %=>% sum

do.call(sum, list(1:3))
`%<%` <- do.call
sum %<% list(1:3)

在本例中,不带括号使用它看起来像是常量.

另一方面,当使用占位符时,需要提供圆括号.

"axc" %>% sub("x", "b", .)

向管道提供不带括号的函数有什么缺点?为其提供空括号的good technical reasons有什么缺点?

推荐答案

但是另一方面,%>%也可以像函数一样使用,并且这些函数通常不带括号地提供.

不,这是令人困惑的:没有单一的方式来"典型地"提供函数,它完全取决于使用情况.

您可以使用sapplydo.call的例子.两者都是higher-order functions,这意味着它们期望函数作为参数.1因为它们期望函数作为参数,所以我们可以传递一个引用函数的名称.但我们也可以不传递名称,而是传递一个任意的expression,它的计算结果是一个函数.

…事实上,不要因为您在示例中传递了一个名称而感到不安,它是一个转移注意力的问题.以下是我们传递表达式(返回函数)的结果的示例:

make_adder = function (y) {
    function (x) x + y
}

sapply(1 : 3, make_adder(2))

但这可能会分散注意力,因为%>%不希望函数对象作为它的第二个参数.相反,它预计会有function call expression分.

在上面的示例中,sapply是一个常规函数,它使用standard evaluation计算其参数.计算其参数1 : 3make_adder(2),并将结果作为参数传递给sapply.2

%>%不是常规函数:它取消对第二个参数的标准求值.相反,它保留未计算形式的表达式并对其进行操作.它这样做的方式相当复杂,但在最简单的情况下,它将第一个参数注入到表达式中,然后对其求值.下面是一些伪代码来说明这一点:

`%>%` = function (lhs, rhs) {
    # Get the unevaluated expression passed as `rhs`
    rhs_expr = substitute(rhs)
    new_rhs_expr = insert_first_argument_into(rhs_expr, lhs)
    eval.parent(new_rhs_expr)
}

这适用于any个有效的rhs表达式:sum()head(3)等.%>%将它们分别转换为sum(lhs)sum(lhs, 3)等,并计算结果表达式.

到目前为止,这是完全一致的.然而,%>%的作者 Select 允许使用additional,这是完全不同的用法:您也可以传递一个简单的名称,而不是将函数调用表达式作为rhs传递.在这种情况下,%>%做的事情完全不同.它不是构造注入lhs的新调用表达式并对其求值,而是直接调用rhs(lhs):

`%>%` = function (lhs, rhs) {
    rhs_expr = substitute(rhs)

    if (is.name(rhs_expr)) {
        rhs(lhs)
    } else {
        # (code from above.)
    }
}

换句话说,%>%接受两种根本不同类型的论点作为rhs,并为它们做不同的事情.

这本身还不是问题.如果我们把function factory作为rhs分,那becomes就是个问题了.这是一个高阶函数,它本身返回一个函数.make_adder从上面就是这样一个功能工厂.

那么:1 : 3 %>% make_adder(2)是做什么的?…

Error in make_adder(., 2) : unused argument (2)

哦,对了!make_adder(2)是一个函数调用表达式,所以%>%的第一个定义适用:转换表达式并计算它.所以它try 计算make_adder(2, 1 : 3),但失败了,因为make_adder只需要一个参数.

幸运的是,为了我们的理智,我们canmake_adder%>%.这甚至不需要额外的规则或文档.稍微想一想,它直接来自上面的第一个定义:我们需要添加另一层函数调用,因为我们希望%>%调用returned乘以make_adder的函数.以下是工作原理:

1 : 3 %>% make_adder(2)()
# 3 4 5

%>%lhs进行内插,使new_rhs变成make_adder(2)(1 : 3).

我们可以通过将返回值make_adder(2)赋给一个名称来使其更具可读性:

add_2 = make_adder(2)

1 : 3 %>% make_adder(2)()      # (1)
#         \___________/
#               v
#             /‾‾‾\
1 : 3 %>%     add_2()          # (2)

我们在这里直接用新引入的名称替换子表达式.这是一个极其基本的计算机科学概念,但它的威力如此之大,以至于它有自己的名字:referential transparency.这是一个使程序推理变得更容易的概念,因为我们知道,我们总是可以将任意子表达式赋给一个名称,并在代码片段中使用该名称来代替它:(1)和(2)是相同的.

但是,实际上,引用透明性要求我们还可以反向进行替换,即用它所引用的值替换名称.果然,这起作用了,我们得到了原来的表达式:

1 : 3 %>%     add_2()          # (1)
#             \___/
#               v
#         /‾‾‾‾‾‾‾‾‾‾‾\
1 : 3 %>% make_adder(2)()      # (2)

(1)和(2)仍然相同.

但不幸的是,it does not always work:

1 : 3 %>%     add_2            # (1)
#             \___/
#               v
#         /‾‾‾‾‾‾‾‾‾‾‾\
1 : 3 %>% make_adder(2)        # (2)

(1)有效,但(2)失败,即使我们只用它的定义替换了add_2.%>%不保留引用透明度.3

这就是为什么在RHS上不使用括号是不一致的,也是为什么它被广泛反对(例如by the tidyverse style guide)的原因.这也是(据我所知)R核心开发人员决定|>总是需要函数调用表达式作为其RHS的原因,并且您不能省略括号.


1我们有一个专门的词来描述这个概念,因为接受函数作为参数在主流编程语言中曾经非常少见.

2这是一种简化.真相更为复杂,但在这里却无关紧要.如果你很好奇,看看R Language Definition: Argument evaluation.

3违反R中的引用透明度非常容易,因为R让我们可以很好地控制如何计算表达式.这通常是非常方便的.但如果不小心使用,它可能会导致混乱的代码和细微的错误,建议仔细权衡违反引用透明度的情况和好处.

R相关问答推荐

如何使用Cicerone指南了解R Shiny中传单 map 的元素?

R的GG平行坐标图中的排序变量

有没有一种方法可以从函数中创建一个值的列表,然后将这些值变成R中的直方图?我一直觉得不行

ggplot2中的X轴显示数值,单位为百,而不是十

用相同方法得到不同函数的ROC最优截断值

如何通过Docker部署我的shiny 应用程序(多个文件)

在另存为PNG之前隐藏htmlwidget绘图元素

为什么我的基准测试会随着样本量的增加而出现一些波动?

如何用书面利率绘制geom_bar图

过滤名称以特定字符串开头的文件

减go R中列表的所有唯一元素对

在R中,如何将误差条放置在堆叠的每个条上?

在R中的数据框上使用Apply()函数时,如何保留非数字列?

如何在使用因子时获得Sankey图的Scale_Fill_Viridis的全范围

如何根据其他列中的两个条件来计算数据帧中的行之间的差异?

排序R矩阵的行和列

具有自定义仓位限制和计数的GGPLATE直方图

使用列名和r中的前缀 Select 列的CREATE函数

如何在R中的两列以上使用联合(&U)?

使用dplyr删除具有条件的行