如何覆盖 magrittr 管道运算符?

How to override magrittr pipe operator?

假设我有一个数据集,我想使用管道语法对其应用多个过滤器,如下所示:

library(magrittr)
library(dplyr)
mtcars %<>% 
  filter(cyl == 4) %>% 
  select(cyl, mpg)
nrow(mtcars)
#[1] 11

我在每个这样的步骤之后用 nrow 检查数据集的当前状态,所以我想我可以沿着

的线路覆盖管道 %<>% 运算符
`%<?>%` <- function(lhs, rhs) {
  x <- magrittr::`%<>%`(lhs, rhs)
  if (is.data.frame(x) & pipeVerbose) print(nrow(x))
}
#Using this will generate errors like
#Error in pipes[[i]] : subscript out of bounds

现在通过打开或关闭 pipeVerbose 标志,我可以控制整个流程的跟踪过程。显然不是那么简单,因为内部评估机制,如提示。问题是,是否有可能以最小的努力实现所需的行为,即不修补 magittr 内部结构?

我不得不承认整个想法有点令人不安,但我的实际情况有点复杂,我想通过一个简单的 on/off 隐藏一些 debug/development 演示目的的详细信息切换。

您可以使用一个 TaskCallback,它会在顶层任务完成时执行。在回调中检查表达式是否包含 %<>% 运算符,如果是则打印结果:

printAssignmentPipe <- function(exp, res, success, printed){

  if (any(grepl("%<>%", exp, fixed = T))) {
    print(res)
  }
  TRUE
}

addTaskCallback(printAssignmentPipe)

您可以轻松地扩展回调以检查 pipeVerbose 的值,或者您只需将 addTaskCallbackremoveTaskCallback 调用到 activate/deactivate。

由于链利用惰性评估,更好的翻译应该是这样的:

`%<?>%` <- function(lhs, rhs) {
  call <- match.call()
  call[[1]] <- quote(`%<>%`)
  x <- eval.parent(call)
  if (is.data.frame(x) & pipeVerbose) print(nrow(x))
}

我们基本上重写了函数调用并对其进行评估。

请注意,您可以 on/off 以这种方式切换,并不比使用 %<?>% 而不是 %<>% 更明显:

p <- function(x){if(pipeVerbose) print(nrow(x))}

pipeVerbose <- FALSE
mtcars %<>% 
  filter(cyl == 4) %>% 
  select(cyl, mpg) %T>% p

rm(mtcars)
pipeVerbose <- TRUE
mtcars %<>% 
  filter(cyl == 4) %>% 
  select(cyl, mpg) %T>% p