如何覆盖magrittr管道运算符?

3

假设我有一个数据集,希望使用管道语法应用多个过滤器:

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 内部的情况下进行操作?
我必须承认整个想法有点令人不安,但我的实际场景更加复杂,并且我想通过一个简单的开关来隐藏一些调试/开发细节以供演示目的。

1
如果我理解正确的话,您希望在管道的每个步骤上打印nrow的输出? - Colin FAY
@ColinFAY 不完全正确;只有在使用 %<>% 重新赋值时才会发生,而且不需要在每个地方都添加额外的管道步骤,比如 %>% nrow - tonytonov
1
可能相关:rmonad - Hong Ooi
3个回答

3

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

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

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

addTaskCallback(printAssignmentPipe)

您可以轻松扩展回调函数,以检查pipeVerbose的值,或者只需调用addTaskCallbackremoveTaskCallback来激活/停用。


2
由于链式操作利用了惰性求值,更好的翻译应该是这样的:
`%<?>%` <- function(lhs, rhs) {
  call <- match.call()
  call[[1]] <- quote(`%<>%`)
  x <- eval.parent(call)
  if (is.data.frame(x) & pipeVerbose) print(nrow(x))
}

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

1
请注意,您可以通过这种方式拥有开/关开关,与使用%<>% 相比,不会更加明显:%<?>%
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

谢谢,这差不多就是我最终想到的。 - tonytonov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接