我使用了%>%
(magrittr
管道),在answer中为RHS提供了一个不带空括号的函数,并得到了一条评论:* 推荐的约定是为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", .)
我想知道在管道中提供一个没有括号的函数时有什么缺点,以及提供空括号的 * 好的技术原因 * 是什么?
1条答案
按热度按时间o8x7eapl1#
但另一方面,
%>%
也可以像函数一样使用,这些函数通常不带括号。不,这是令人困惑的事情:“通常提供”功能的方式并不单一,它完全取决于用途。
我们使用
sapply
和do.call
的例子。两者都是higher-order functions,这意味着它们期望函数作为参数。1由于它们期望函数作为参数,我们可以传递一个引用函数的名称。但是我们也可以传递一个任意的 expression,而不是一个名字,它的计算结果是一个函数。事实上,不要因为在示例中传递了一个名字而感到困惑,这是一个转移注意力的东西。这里有一个例子,我们传递了一个表达式的结果(返回一个函数):
但这可能会分散注意力,因为
%>%
不期望函数对象作为其第二个参数。相反,它需要一个 * 函数调用表达式 *。在上面的示例中,
sapply
是一个常规函数,它使用 * 标准求值 * 来计算其参数。它的两个参数1 : 3
和make_adder(2)
都被计算,结果作为参数传递给sapply
。2%>%
不是一个常规函数:它抑制了第二参数的标准求值。相反,它将表达式保持为未计算的形式并对其进行操作。它的实现方式相当复杂,但在最简单的情况下,它将第一个参数注入表达式,然后对其求值。下面是一些伪代码来说明这一点:这适用于 * 任何 * 有效的
rhs
表达式:sum()
、head(3)
等%>%
分别将它们转换为sum(lhs)
、sum(lhs, 3)
等,并计算结果表达式。到目前为止,这是完全一致的。然而,
%>%
的作者选择允许一个 * 额外的 *,完全不同的用法:你也可以传递一个简单的名字,而不是像rhs
那样传递函数调用表达式。在这种情况下,%>%
会做一些完全不同的事情。它没有构造一个新的调用表达式来注入lhs
并对其求值,而是直接调用rhs(lhs)
:换句话说,
%>%
与rhs
接受两种根本不同类型的参数,并为它们做不同的事情。这本身还不是一个问题。如果我们传递一个 * 函数工厂 * 作为
rhs
,这就 * 成为 * 一个问题。这是一个高阶函数,它本身返回一个函数。上面的make_adder
就是这样一个函数工厂。1 : 3 %>% make_adder(2)
是什么?...哦,对了!
make_adder(2)
是一个函数调用表达式,所以%>%
的第一个定义适用:转换表达式并对其求值。所以它试图计算make_adder(2, 1 : 3)
,但失败了,因为make_adder
只需要一个参数。幸运的是,我们可以将
make_adder
与%>%
一起使用。这甚至不需要额外的规则或文档。稍微思考一下,它直接遵循上面的第一个定义:我们需要添加另一层函数调用,因为我们希望%>%
调用make_adder
* 返回 * 的函数。以下工程:%>%
对lhs
进行插值,使得new_rhs
变为make_adder(2)(1 : 3)
。我们可以通过将
make_adder(2)
的返回值分配给一个名称来使其更具可读性:我们直接用新引入的名称替换子表达式。这是一个非常基本的计算机科学概念,但它是如此强大,以至于它有自己的名字:referential transparency。这是一个使程序推理更容易的概念,因为我们知道我们总是可以将任意子表达式分配给一个名称,并在一段代码中使用该名称:(1)和(2)是相同的。
但是,实际上,引用透明性要求我们也可以反向进行替换,即。将名称替换为它所引用的值。果然,这是可行的,我们得到了原始的表达式:
(1)和(2)仍然相同。
但不幸的是它并不总是有效:
(1)工作,但(2)失败,即使我们只是用它的定义替换
add_2
。%>%
不保留引用透明性。3这就是为什么在RHS上不使用括号是不一致的,也是为什么它被广泛地劝阻(例如。《易经·系辞下》:这也是(据我所知)为什么R核心开发人员决定
|>
总是需要一个函数调用表达式作为其RHS,并且不能省略括号。1我们有一个专门的词来描述这个概念,因为接受函数作为参数在主流编程语言中非常罕见。
2这是一种简化。真相更复杂,但与此无关。如果你有兴趣,请参阅R语言定义:论证评估。
3在R中违反引用透明性是很容易的,因为R给了我们很多控制如何计算表达式的控制权。这通常是非常方便的。但是如果不小心使用,它可能会导致代码混乱和细微的错误,建议仔细权衡违反引用透明性的好处。