显式替换占位符的管道可以(相当)直接地实现,因为基本R函数substitute
实现了占位符的大部分.需要注意的是,substitute
需要一个不带引号的表达式,因此我们需要解决这个问题,而且我们显然需要计算结果表达式:
`%>%` = function (lhs, rhs) {
subst = call('substitute', substitute(rhs), list(. = lhs))
eval.parent(eval(subst))
}
这里我重复使用了%>%
运算符,但如果您担心与‘magrittr’的冲突,您显然可以使用另一个运算符,例如%|%
.
1:5 %>% sum(na.rm = TRUE, .) %>% identity()
# Error in identity() : argument "x" is missing, with no default
1:5 %>% sum(na.rm = TRUE, .) %>% identity(.)
# [1] 15
以上用由LHS提供的值在RHS中每次出现.
的情况下起作用replacing.也就是说,在判断.
is not a name期间.这通常是expected和desired的语义.然而,这意味着您不能在RHS内分配to .
(包括调用替换函数),因为.
不是一个名称.
所以像{names(.) = "foo"; .}
这样的东西是行不通的.
我们可以用不同的实现来解决这个问题,它不会用LHS替换.
,而是在判断RHS的环境中定义.
:
`%>%` = function (lhs, rhs) {
eval_env = new.env(parent = parent.frame())
eval_env$. = lhs
eval(substitute(rhs), envir = eval_env)
}
现在我们可以在作业(job)中使用.
作为名称:
1 %>% {names(.) = "foo"; .}
# foo
# 1
However,其他一些方法不再起作用,因为我们现在在不同的环境中计算该表达式:
1:5 %>% assign("x", .)
x
# Error: object 'x' not found
…而这did与第一个管道实现一起工作.我们could通过将.
直接注入调用环境使其再次工作,但这会扰乱用户环境,我strongly不鼓励这样做.1
相反,如果您想要在管道表达式中调用assign
之类的操作,请明确表示要分配给哪个环境(例如,将envir = the_environment
传递到assign
).
1您可以通过仔细保存用户状态并在事后进行清理来避免扰乱用户环境;但这会导致更复杂(且容易出错)的实现:
`%>%` = function (lhs, rhs) {
caller = parent.frame()
if (exists('.', envir = caller, inherits = FALSE)) {
stored_dot = caller$.
on.exit({caller$. = stored_dot})
} else {
on.exit(rm(., envir = caller))
}
caller$. = lhs
eval.parent(substitute(rhs))
}