【问题标题】:Does the Bizarro pipe ->.; have disadvantages making it not recommended for use?Bizarro 管道是否 ->.;有不推荐使用的缺点吗?
【发布时间】:2021-08-24 08:11:22
【问题描述】:

自 R 版本 4.1.0 以来,管道 |> 处于稳定版本中。当将 lhs 传递给除第一个参数之外的参数时,手册的示例显示:

mtcars |> subset(cyl == 4) |> (function(d) lm(mpg ~ disp, data = d))()

或者当使用\(x)

mtcars |> subset(cyl == 4) |> (\(d) lm(mpg ~ disp, data = d))()

或者使用当前需要激活的PIPEBIND

Sys.setenv(`_R_USE_PIPEBIND_` = TRUE) 
mtcars |> subset(cyl == 4) |> . => lm(mpg ~ disp, data = .)

除了|>Bizarro pipe ->.; 也可以像这样使用

mtcars |> subset(cyl == 4) ->.; lm(mpg ~ disp, data = .)

R 中管道符号的一个目的是允许以一种可以使处理步骤序列的方式编写嵌套的调用序列 更容易遵循 ,至少对我来说,这也是->.;实现的。 Bizarro 管道并不是真正的管道,但对我来说,它目前是|> 的一个受欢迎的替代品,尤其是在将 lhs 传递给第一个以外的参数的情况下。但是在使用它时,我得到comments 不使用它。

所以我想知道 Bizarro 管子是否有缺点建议不要使用它?


到目前为止,我看到它在环境中创建或覆盖了.,并且 保留此引用,这将在修改时强制复制。但是当调用一个函数时,参数中有数据,也会创建对该数据的引用。而当使用for 循环时,var 在使用后仍然存在。

for(i in iris) {}
tracemem(i) == tracemem(iris[[ncol(iris)]])
#[1] TRUE

在性能方面也没有太大的劣势:

x <- 42
library(magrittr)
Sys.setenv(`_R_USE_PIPEBIND_` = TRUE) 
#Nonsense operation to test Performance
bench::mark(x
, identity(x)
, "x |> identity()" = x |> identity()
, "x |> (\\(y) identity(y))()" = x |> (\(y) identity(y))()
, "x |> . => identity(.)" = x |> . => identity(.)
, "x ->.; identity(.)" = {x ->.; identity(.)}
, x %>% identity
)
#  expression                     min   median `itr/sec` mem_alloc `gc/sec` n_itr
#  <bch:expr>                <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int>
#1 x                          60.07ns  69.03ns 13997474.        0B      0   10000
#2 identity(x)               486.96ns 541.91ns  1751206.        0B    175.   9999
#3 x |> identity()           481.03ns 528.06ns  1812935.        0B      0   10000
#4 x |> (\(y) identity(y))() 982.08ns   1.08µs   854349.        0B     85.4  9999
#5 x |> . => identity(.)     484.06ns 528.06ns  1815336.        0B      0   10000
#6 x ->.; identity(.)        711.07ns 767.99ns  1238658.        0B    124.   9999
#7 x %>% identity              2.86µs   3.23µs   294945.        0B     59.0  9998

【问题讨论】:

  • 这个问题似乎特别关注性能。正如我在回答中解释的那样,性能不是问题。我有点困惑,你会立即想到这一点。
  • 也可以写成:mtcars |&gt; subset(cyl == 4) |&gt; lm(formula = mpg ~ disp)。我认为性能是重点的原因是它似乎是 |> 背后的主要激励因素,它放弃了 %>​​% 和 bizarro 管道的大部分功能,以便通过语法转换来实现它。我怀疑关于 bizarro 管道的副作用论点是否真的具有实际意义。
  • @G.Grothendieck 是的,当占位符之前的所有参数都被填满时,可以避免使用占位符。但有时这会以大量的文字告终。
  • @G.Grothendieck 我希望让人们相信这具有实际重要性:故意编写比可能的更健壮的代码只是一个难以置信 坏主意:记住“one in a million is next Tuesday” 的软件工程格言:由于规模的原因,听起来不太可能的错误总是导致问题。并且应始终将隐藏错误以静默方式产生错误结果的可能性降至最低。

标签: r


【解决方案1】:

bizarro 管道的主要问题是它会产生隐藏的副作用,并且更容易产生细微的错误。它降低了代码的可维护性。

. 变量的持续存在使得以后很容易意外地引用这个值:如果你在某个时候忘记分配给它,它的存在会掩盖错误并且认为你做到了。很容易排除这种可能性,但这样的错误相当普遍,更糟糕的是,非常不明显:你不会收到错误消息,只会得到错误的结果。相比之下,如果您在某处忘记了管道符号,您会立即收到一条错误消息。

更糟糕的是,奇怪的管道隐藏这种容易出错的副作用以两种不同的方式。首先,因为它使分配不明显。 I’ve argued previously 不应该使用 -&gt; 赋值,因为从左到右的赋值隐藏了副作用,并且副作用应该在语法上很明显。这种情况下的副作用是赋值,它应该发生在它最突出的地方:在表达式的第一列,而不是在它的末尾隐藏。这是对使用-&gt;(或任何其他掩盖副作用的尝试)的基本反对意见,不仅限于 bizarro 管道。

而且因为. 默认情况下隐藏(来自ls 和IDE 的检查器窗格),这使得意外依赖它变得更加容易。

因此,如果您想分配一个临时名称而不是使用管道,只需执行此操作即可。但是:

  1. 执行从右到左的赋值,即使用name = valuename &lt;- value value -&gt; name
  2. 使用描述性名称。

我怎么强调都不过分,这是一个真正的细微错误来源——不要低估它!

另一个问题是它的使用破坏了编辑器对自动格式化代码的支持。这在某些 IDE 中通过插件是一个“可解决的问题”,但该解决方案实际上解决了一个甚至不应该存在的问题。为了澄清我的意思,如果您使用的是 bizarro 管道,您可能想要一个悬挂缩进,即类似以下内容:

mtcars ->.
  subset(cyl == 4) ->.
  lm(mpg ~ disp, data = .)

...但是自动缩进不会像这样缩进代码,并且自动格式化程序会将悬挂缩进变平。

这些问题都不是令人望而却步的(尽管第一个问题相当严重);但是在没有积极的论据支持使用 bizarro 管道的情况下,他们果断地打破了平衡。毕竟,通过 proper 管道操作符1 或常规赋值不能更好地解决 bizarro 管道解决的问题是什么?如果您不能使用 R 4.1,请使用“magrittr”。如果您不喜欢“magrittr”的语义,编写自己的管道运算符,使用许多其他现有实现中的一种,或者只使用常规赋值。

最后,有人可能会争辩说,这段代码非常不寻常,足以绊倒读者,但老实说,如果用法是一致的并且在某处在某处有明确记录,我认为这不是一个非常有说服力的论点。但它提出了另一个反对向初学者推荐使用它的论点。


1 当然这很容易回答:|&gt; 不允许显式点替换。虽然我理解反对支持它的论点,但它的缺失会鼓励诸如 bizarro pipe 之类的黑客攻击,这是一个非常有力的论据,表明这实际上是一个巨大的错误。

【讨论】:

  • 感谢您的深入回答!我最近发现存在一个管道绑定,需要激活它,它允许替换。我已将这种方式包含在问题中,并帮助我不使用 ->.;.
  • @GKi 我在邮件列表中找不到任何东西,但我认为 pipebind 需要通过环境变量激活的原因意味着该功能是实验性的,可能会在未来的版本中被删除。邮件列表中几乎没有人喜欢 pipebind 功能,而且它真的很奇怪。
  • 是的,当然,在当前阶段,看起来最好不要使用|&gt; =&gt;,因为它可能会改变。但至少这意味着他们正在研究它,并且有可能在基础上有替换的管道。
猜你喜欢
  • 2019-02-28
  • 2021-08-01
  • 2011-01-25
  • 1970-01-01
  • 2017-01-10
  • 2015-01-30
  • 1970-01-01
  • 1970-01-01
  • 2011-06-22
相关资源
最近更新 更多