我应该避免使用管道操作符编程包吗?
Should I avoid programming packages with pipe operators?
有什么 objective 原因可以解释为什么当我在 R 中编写程序包时应避免来自 R 程序包 magrittr
的 pipe operators,例如 %>%
?
更具体地说,我想知道使用管道运算符是否会导致编码冲突或(正面或负面)影响性能。我正在寻找此类案例的具体示例。
像所有用 R 编写的高级函数一样,%>%
会带来很多开销,所以不要在循环中使用它(这包括隐式循环,例如 *apply
系列,或每组在 dplyr
或 data.table
等包中循环)。这是一个例子:
library(magrittr)
x = 1:10
system.time({for(i in 1:1e5) identity(x)})
# user system elapsed
# 0.07 0.00 0.08
system.time({for(i in 1:1e5) x %>% identity})
# user system elapsed
# 15.39 0.00 16.68
不应该对包添加依赖项掉以轻心。一般来说,您的包所依赖的每个包在依赖项更新时都存在未来维护的风险,或者以防依赖关系停止维护。它还使人们(稍微)更难安装您的软件包 - 尽管只有在互联网连接不可靠或某些软件包更难以安装在某些系统或硬件上的情况下才会明显。但是如果有人想把你的包放在拇指驱动器上安装在某个地方,他们还需要确保他们拥有你的所有依赖项(以及你的依赖项的依赖项......)。
Base R 和默认包历史悠久,R-Core 非常注意不要引入会破坏下游依赖性的更改。 magrittr
更新很多,看起来像是 2014 年 2 月首次出现在 CRAN 上。
实际上,magrittr
一直很稳定,似乎是低风险依赖。特别是如果您只导入 %>%
并忽略它提供的更深奥的运算符(如 dplyr
、tidyr
等所做的那样),您可能非常安全。它的流行几乎可以保证即使它的创造者放弃它,也会有人接手维护。
现在,在 2022 年,我们已经发布了几个具有基本管道 |>
的 R 版本,因此只要您可以 运行 R 4.1.0 版或更大。
与 "standard functional programming" 相比,管道范式颠倒了函数应用的明显顺序。这是否会产生不利后果取决于功能符号学(我最初的错误意图是 'semantics' 但 spielchucker 虽然我的意思是 semiotics
并且看起来没问题)。我碰巧认为管道创建的代码可读性较差,但那是因为我已经训练我的大脑从 "inside-out" 看编码。比较:
y <- func3 ( func2( func1( x) ) )
y <- x %>% func1 %>% func2 %>% func3
根据我的想法,第一个更具可读性,因为信息 "flows" 向外(并且始终向左)并最终位于最左边的位置 y
,而第二个中的信息一个向右流动然后“转身并被发送到左边。管道范式还允许无参数函数应用程序,我认为这增加了出错的可能性。仅使用位置参数匹配的 R 编程通常会产生完全错误的错误消息难以理解,而训练自己总是(或几乎总是)使用参数名称有利于提供更多信息的错误消息。
我更喜欢具有一致方向的管道范例:
y <- func3 %<% func2 %<% func1 %<% x
# Or
x %>% func1 %>% func2 %>% func3 -> y
我认为这实际上是 pkg-magrittr
原始设计的一部分,我相信它包括 'left-pipe' 和 'right-pipe'。所以这可能是一个人为因素的设计问题。 R 具有从左到右的关联性,dplyr/magrittr 管道范式的典型用户通常遵守该规则。我可能是脑僵症,你们这些年轻人可能就是未来,你们自己选吧。我真的很钦佩 Hadley 的数据输入和处理合理化目标,以便文件和 SQL 服务器被视为通用串行设备。
David Robinson 提供的示例表明跟踪参数是一个大问题,我完全同意。我通常的做法是使用制表符和空格来突出层次结构:
func3 ( func2(
func1(x, a), # think we need an extra comma here
b, c), # and here
d, e, f)
x %>% func1(a) %>% func2(b, c) %>% func3(d, e, f)
诚然,在检查缺少逗号或括号时,使用语法感知编辑器可以更轻松地做到这一点,但在上面的示例中,stacking/spacing 方法确实突出显示了我认为是语法错误。 (遇到困难时我也会快速添加参数名称,但我认为这同样适用于管道代码策略。)
有什么 objective 原因可以解释为什么当我在 R 中编写程序包时应避免来自 R 程序包 magrittr
的 pipe operators,例如 %>%
?
更具体地说,我想知道使用管道运算符是否会导致编码冲突或(正面或负面)影响性能。我正在寻找此类案例的具体示例。
像所有用 R 编写的高级函数一样,%>%
会带来很多开销,所以不要在循环中使用它(这包括隐式循环,例如 *apply
系列,或每组在 dplyr
或 data.table
等包中循环)。这是一个例子:
library(magrittr)
x = 1:10
system.time({for(i in 1:1e5) identity(x)})
# user system elapsed
# 0.07 0.00 0.08
system.time({for(i in 1:1e5) x %>% identity})
# user system elapsed
# 15.39 0.00 16.68
不应该对包添加依赖项掉以轻心。一般来说,您的包所依赖的每个包在依赖项更新时都存在未来维护的风险,或者以防依赖关系停止维护。它还使人们(稍微)更难安装您的软件包 - 尽管只有在互联网连接不可靠或某些软件包更难以安装在某些系统或硬件上的情况下才会明显。但是如果有人想把你的包放在拇指驱动器上安装在某个地方,他们还需要确保他们拥有你的所有依赖项(以及你的依赖项的依赖项......)。
Base R 和默认包历史悠久,R-Core 非常注意不要引入会破坏下游依赖性的更改。 magrittr
更新很多,看起来像是 2014 年 2 月首次出现在 CRAN 上。
实际上,magrittr
一直很稳定,似乎是低风险依赖。特别是如果您只导入 %>%
并忽略它提供的更深奥的运算符(如 dplyr
、tidyr
等所做的那样),您可能非常安全。它的流行几乎可以保证即使它的创造者放弃它,也会有人接手维护。
现在,在 2022 年,我们已经发布了几个具有基本管道 |>
的 R 版本,因此只要您可以 运行 R 4.1.0 版或更大。
与 "standard functional programming" 相比,管道范式颠倒了函数应用的明显顺序。这是否会产生不利后果取决于功能符号学(我最初的错误意图是 'semantics' 但 spielchucker 虽然我的意思是 semiotics
并且看起来没问题)。我碰巧认为管道创建的代码可读性较差,但那是因为我已经训练我的大脑从 "inside-out" 看编码。比较:
y <- func3 ( func2( func1( x) ) )
y <- x %>% func1 %>% func2 %>% func3
根据我的想法,第一个更具可读性,因为信息 "flows" 向外(并且始终向左)并最终位于最左边的位置 y
,而第二个中的信息一个向右流动然后“转身并被发送到左边。管道范式还允许无参数函数应用程序,我认为这增加了出错的可能性。仅使用位置参数匹配的 R 编程通常会产生完全错误的错误消息难以理解,而训练自己总是(或几乎总是)使用参数名称有利于提供更多信息的错误消息。
我更喜欢具有一致方向的管道范例:
y <- func3 %<% func2 %<% func1 %<% x
# Or
x %>% func1 %>% func2 %>% func3 -> y
我认为这实际上是 pkg-magrittr
原始设计的一部分,我相信它包括 'left-pipe' 和 'right-pipe'。所以这可能是一个人为因素的设计问题。 R 具有从左到右的关联性,dplyr/magrittr 管道范式的典型用户通常遵守该规则。我可能是脑僵症,你们这些年轻人可能就是未来,你们自己选吧。我真的很钦佩 Hadley 的数据输入和处理合理化目标,以便文件和 SQL 服务器被视为通用串行设备。
David Robinson 提供的示例表明跟踪参数是一个大问题,我完全同意。我通常的做法是使用制表符和空格来突出层次结构:
func3 ( func2(
func1(x, a), # think we need an extra comma here
b, c), # and here
d, e, f)
x %>% func1(a) %>% func2(b, c) %>% func3(d, e, f)
诚然,在检查缺少逗号或括号时,使用语法感知编辑器可以更轻松地做到这一点,但在上面的示例中,stacking/spacing 方法确实突出显示了我认为是语法错误。 (遇到困难时我也会快速添加参数名称,但我认为这同样适用于管道代码策略。)