【发布时间】:2021-12-11 16:52:08
【问题描述】:
我想要一个粘贴逻辑表达式的函数
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
两者都符合预期。
关于如何改进这段代码并实现第二个目标(在返回的表达式中取消引用),我有几个问题。
第一季度。在这部分最后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>?
第二季度。我必须使用逻辑运算符的前缀函数版本,因此最终表达式是对.Primitive("&") 的递归函数调用。
有没有办法将& 和| 作为函数外部的符号传递,所以我得到最终表达式为expr(a == b & c > q & f < g)?
简单地将& 和| 与ensym 或enexpr 包装在函数体内会产生如下错误:Error: unexpected '&' in "expr(&"
第三季度。此解决方案不支持在返回的表达式中进一步取消引用,例如
expr(a == !!b & c > !!q & f < !!g)
因为每个dots[[i]] 都是像a == b 这样的单个表达式,我无法进一步分解和操作。定义不被引用的一面甚至更难完成。有什么简单的方法可以实现吗?
【问题讨论】:
-
您能否发布您实际尝试解决的问题的摘要?我不相信你会以正确的方式去做。
-
@wurli,我要解决的问题是我要编写的函数不言自明:我想使用函数的方式将几个单个逻辑表达式粘贴到一个完整的逻辑表达式中。我没有使用任何数据对象。我正在直接使用代码。
-
对不起,我不清楚。你会用这样的功能做什么?或者您只是想了解更多关于 R 元编程的信息?
-
此模式的一个实际示例是
dplyr::filter(),它在内部将...中的输入与布尔运算符连接起来。 -
@wurli 感谢您的关注。我正在使用它来学习 R 中元编程和 rlang 的详细信息。我确实有一个用于构建简单语言解释器的用例。有一段与逻辑有关。为此,我需要首先了解 R 对操作和评估表达式的支持,并尽可能多地关注范围和环境安全。您可以认为这是一个玩具示例。我还有其他玩具示例。
标签: r metaprogramming quote tidyeval quasiquotes