Проблема в том, что foo
не "знает", как был написан канал, он просто знает, как он вызывается, когда magrittr добавил точку (если уместно), поэтому из самой функции мыне может различить неявные и явные точки.
В этом случае мы можем просто переключить 2 первых аргумента, если первый является точкой, и вернуть оцененный модифицированный вызов:
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
Создано в 2019-10-09 пакетом представ. (v0.3.0)
Это необходимо настроить, если 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
Создано в 2019-10-09 в пакете Представить (v0.3.0)
Примечание: конвейерная передача во вторую немного странная, я бы предпочел трубу в последнюю (это будет иметьтот же результат для примера вашего вопроса), если вы хотите передать до конца, вы должны сделать:
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
Создано в 2019-10-09 с помощью пакета представить (v0.3.0)