使用magrittr点占位符IN FUNCTION DEFINITION默认通过管道运算符将对象传递到任意位置的参数?

时间:2019-10-05 19:38:26

标签: r magrittr

我想定义一个函数,使magrittr管道运算符默认情况下将对象传递给第二个参数。

library(magrittr)

foo <- function(a, b) c(a,b)

管道运算符将对象传递给foo的第一个参数。

1 %>% foo(2)

使用.占位符时,管道将对象传递给foo的第二个参数。

1 %>% foo(2, .)

是否可以构建函数,使其在其定义中直接具有.占位符, 以便管道默认使用第二个参数?在伪代码中,这将是 类似于:

foo2 <- function(a, b = <pipe arg placeholder>) {
    b = <process arg placeholder> 
    c(a, b)
}

2 个答案:

答案 0 :(得分:1)

在此示例中,管道的输入用作verilator/README的第二个参数。我们确实必须使用{...},但这也许已经足够接近了。

f

答案 1 :(得分:1)

问题是foo不“知道”管道的写法 它只知道一旦 magrittr 添加了点(如果相关),该如何调用 因此,根据函数本身,我们无法区分隐式点和显式点。

除了这个问题之外,如果第一个参数是点,我们可以切换两个第一个参数,然后返回经过评估的修改后的调用:

library(magrittr)
foo <- function(a, b) {
  mc <- match.call()
  if(mc[[2]] == quote(.)) {
    mc[[2]] <- mc[[3]]
    mc[[3]] <- quote(.)
    return(eval.parent(mc))
  }
  c(a,b)
  }
1 %>% foo(2)
#> [1] 2 1
1 %>% foo(2, .)
#> [1] 2 1
# but also, because of disclaimer above
1 %>% foo(., 2)
#> [1] 2 1

reprex package(v0.3.0)于2019-10-09创建

如果a可以采用默认值并将其留空,则可能需要进行调整。

edit:当我说foo不知道管道的编写方式时,我撒谎了,它在调用堆栈中,我们可以通过在函数中调用sys.call()来看到它,但是我认为解决方案是如此复杂!


另一种方法是定义插入第二个位置的管道,它稍微灵活一些,也许也就不那么令人惊讶了:

foo <- function(a=2, b) {
  c(a,b)
}

`%>2%` <-
  function (lhs, rhs) {
    rhs_call <- insert_dot2(substitute(rhs))
    eval(rhs_call, envir = list(. = lhs), enclos = parent.frame())
  }

insert_dot2 <-
  function(expr, special_cases = TRUE) {
    if(is.symbol(expr) || expr[[1]] == quote(`(`)) {
      # if a symbol or an expression inside parentheses, make it a call with 
      # a missing first argument and a dot on second position
      expr <- as.call(c(expr,alist(x=)[[1]], quote(`.`)))
    } else if(length(expr) ==1) {
      # if a call without arg, same thing
      expr <- as.call(c(expr[[1]],alist(x=)[[1]], quote(`.`)))
    } else if (expr[[1]] != quote(`{`) &&
               all(sapply(expr[-1], `!=`, quote(`.`)))) {
      # if a call with args but no dot in arg, insert dot in second place first
      expr <- as.call(c(as.list(expr[1:2]), quote(`.`), as.list(expr[-(1:2)])))
    }
    expr
  }
1 %>2% foo(2)
#> [1] 2 1
1 %>2% foo(2, .)
#> [1] 2 1
1 %>2% foo(., 2)
#> [1] 1 2
1 %>2% foo()
#> [1] 2 1

reprex package(v0.3.0)于2019-10-09创建


注意:用管道输送到第二位有点奇怪,我宁愿用管道输送到最后(对于您的问题的示例,结果将是相同的),如果您想通过管道输送到最后,则可以:

foo <- function(a=2, b) {
  c(a,b)
}

`%>last%` <-
  function (lhs, rhs) {
    rhs_call <- insert_dot_last(substitute(rhs))
    eval(rhs_call, envir = list(. = lhs), enclos = parent.frame())
  }

insert_dot_last <-
  function(expr, special_cases = TRUE) {
    if(is.symbol(expr) || expr[[1]] == quote(`(`)) {
      # if a symbol or an expression inside parentheses, make it a call with 
      # a dot arg
      expr <- as.call(c(expr, quote(`.`)))
    } else if(length(expr) ==1) {
      # if a call without arg, same thing
      expr <- as.call(c(expr[[1]], quote(`.`)))
    } else if (expr[[1]] != quote(`{`) &&
               all(sapply(expr[-1], `!=`, quote(`.`)))) {
      # if a call with args but no dot in arg, insert dot in last place
      expr <- as.call(c(as.list(expr), quote(`.`)))
    }
    expr
  }
1 %>last% foo(2)
#> [1] 2 1
1 %>last% foo(2, .)
#> [1] 2 1
1 %>last% foo(., 2)
#> [1] 1 2

reprex package(v0.3.0)于2019-10-09创建