我想要一个粘贴逻辑表达式的功能
paste_logic(a == b, c > q, f < g, sep = and)
# should return
# expr(a == b & c > q & f < g)
我还想在 ruturning 期间(而不是在函数调用中)懒惰地取消引用,理想情况下控制哪一边
paste_expr(paste_expr(a == b, c > q, f < g, sep = and, side = right)
# should return
# expr(a == !!b & c > !!q & f < !!g)
我实现第一个目标的解决方案是:
paste_logic <- function(sep, ...) {
dots <- enquos(...)
sep <- enexpr(sep)
dispatch <- function(symbol) if (symbol == expr(and)) `&` else `|`
if (length(dots) == 1) {
dots[[1]]
} else {
expr(`!!`(dispatch(sep))(!!(dots[[1]]), !!paste_logic(sep, !!!dots[-1])))
}
}
paste_logic(and, a > b, c == d, k == f)
# returns
# .Primitive("&")(~a > b, .Primitive("&")(~c == d, ~k == f))
和
a <- 1
b <- 2
c <- 3
d <- 3
k <- 9
f <- 10
eval_tidy(paste_logic(and, a > b, c == d, k == f))
# returns FALSE
eval_tidy(paste_logic(or, a > b, c == d, k == f))
# returns TRUE
两者都符合预期。
关于如何改进这段代码并实现第二个目标(在返回的表达式中取消引用),我有几个问题。
Q1。在最后一个else {...}
闭包中的这一部分:
expr(`!!`(dispatch(sep))(!!(dots[[1]]), !!paste_logic(sep, !!!dots[-1])))
我必须使用素数符号来包装!!
运算符或使用UQ
函数。如果我简单地将它作为!!(dispatch(sep))
或完整的功能定义作为这个
paste_logic <- function(sep, ...) {
dots <- enquos(...)
sep <- enexpr(sep)
dispatch <- function(symbol) if (symbol == expr(and)) `&` else `|`
if (length(dots) == 1) {
dots[[1]]
} else {
expr(!!(dispatch(sep))(!!(dots[[1]]), !!paste_logic(!!sep, !!!dots[-1])))
}
}
paste_logic(or, a > b, c == d, k == f)
它返回错误
Error: Quosures can only be unquoted within a quasiquotation context.
# Bad:
list(!!myquosure)
# Good:
dplyr::mutate(data, !!myquosure)
在全球环境中测试
a <- 1
b <- 2
c <- `&`
expr(!!(c)(!!a, !!n))
工作正常,没有错误并返回TRUE
。那么,为什么在我的代码中这不起作用而我必须使用<prime>!!<prime>
?
Q2。我必须使用逻辑运算符的前缀函数版本,因此最终表达式是对.Primitive("&")
.
有没有办法从函数外部传递&
和|
作为符号,所以我得到最终表达式为expr(a == b & c > q & f < g)
?
简单地包装&
和|
withensym
或enexpr
内部函数体会产生如下错误:Error: unexpected '&' in "expr(&"
第三季度。此解决方案不支持在返回的表达式中进一步取消引用,例如
expr(a == !!b & c > !!q & f < !!g)
因为每个dots[[i]]
都是一个单独的表达式a == b
,我无法进一步分解和操作。定义不被引用的一面甚至更难完成。有什么简单的方法可以实现这一目标吗?