【问题标题】:Can a function detect whether it has been piped into?一个函数可以检测它是否已经被输入?
【发布时间】:2020-09-22 21:47:49
【问题描述】:

有没有办法让函数检测 magrittr 管道是否已将数据传递到其中?

一个激励的例子:

ifelse() 等某些函数不适用于 magrittr 输入。

1:4 %>% ifelse(. < 3, "low", "high")
#> Error in ifelse(., . < 3, "low", "high") : unused argument ("high")

导致错误。

要使功能按预期工作,您必须使用

1:4 %>% {ifelse(. < 3, "low", "high")}
#> "low"  "low"  "high" "high"

有没有办法重新定义一个函数,使其在管道输入时表现不同?

我的最佳尝试:

my_ifelse <- function(...) {
  args <- list(...)

  if (sys.call()[[2]] == ".") args <- args[-1]

  ifelse(test = args[[1]], yes = args[[2]], no = args[[3]])
}

1:4 %>% my_ifelse(. < 3, "low", "high")
#> "low"  "low"  "high" "high"

这个解决方案有什么问题吗?会不会意外坏掉?

编辑:显然,如果传递的参数数量不正确,这将返回错误。更成熟的方法会尝试提供信息错误,甚至可能允许使用参数名称而不是 ...

【问题讨论】:

  • 未经测试,但我认为my_ifelse &lt;- function(x, ...) ifelse(...) 应该可以工作。如果没有丢失,我相信你的不起作用。
  • @Moody_Mudskipper 这是一个出色而简单的解决方案!一个缺点是,当它没有通过管道传输时,它无法复制通常的 ifelse() 功能。但是,如果您知道该函数将始终通过管道传输,那么这比我的通行证要好得多!
  • 您也可以定义自己的不插入隐式点的管道:"%&gt;&gt;%" &lt;- function(lhs, rhs) eval(substitute(rhs), envir = list(. = lhs), enclos = parent.frame())

标签: r magrittr


【解决方案1】:

当我们使用管道时,我们知道管道的左侧被视为右侧函数的第一个参数。

所以你不能指望这会起作用:

1:4 %>% ifelse(. < 3, "low", "high")

因为这实际上意味着

ifelse(1:4, 1:4 < 3, "low", "high")

这会给你完全相同的错误

ifelse(1:4, 1:4

是的,您可以通过在管道周围使用{} 来停止此默认行为。

我不确定您的最终目标是什么,但可以遵循的一般规则是您想要对输入进行的所有更改都应仅在 LHS 端完成,除非您想使用 {}

所以这行得通:

(1:4 < 3) %>% ifelse("low", "high")
#[1] "low"  "low"  "high" "high"

【讨论】:

  • 另一个可能有助于 OP 理解的示例:sum(x &gt; 2) 是找出x 有多少元素大于 2 的标准方法,sum(1:4 &gt; 2) 正确给出 2。当然@987654330 @ 没有得到正确答案,我们得到 12,因为它变成了 sum(1:4, 1:4 &gt; 2),即 sum(10, 2),即 12。但这与函数的编写方式没有任何关系 - 它与 1:4 %&gt;% sum(. &gt; 2) 试图通过管道传递部分参数而不是整个事物这一事实有关。
  • 这是magrittris called out in the readme 的预期行为:“在右侧表达式中多次使用占位符. 很简单。但是,当占位符仅出现在嵌套表达式中 magrittr 仍将应用第一个参数规则。原因是在大多数情况下,这会产生更干净的代码。”,示例如下。因此,管道可以重写以按照 OP 的要求工作,而不会更改管道输入的功能。
猜你喜欢
  • 2014-08-08
  • 1970-01-01
  • 2020-08-28
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 2020-07-01
  • 2011-07-06
  • 1970-01-01
相关资源
最近更新 更多