在捕获环境时进行操作后以点的形式转发表达式

时间:2017-05-05 13:55:07

标签: r nse rlang tidyeval

我有一个函数fun_1在其substitute()参数上使用...,另一个函数fun_2使用签名fun_2(...)来实现模式{{1} }}。我希望do.call(fun_1, dots)内的fun_1()看到fun_2()传递给...。这是我想要做的事情的例证。

fun_2()

我认为fun_1 <- function(...) { substitute(list(...))[-1] %>% sapply(deparse) } foo <- "X" bar <- "Y" fun_1(foo, bar) # [1] "foo" "bar" fun_2 <- function(...) { # dots <- Filter(length, ???) # rlang::invoke(my_fun, dots) } fun_2(foo, bar, NULL) # desired output: # [1] "foo" "bar" 有足够的魔力使这项工作成功但我无法弄清楚如何运作。只要

,我就可以修改rlang
  1. fun_1可以访问fun_1()foo
  2. 的值
  3. bar 模式在do.call
  4. 中实现

    编辑:我还需要fun_2()才能工作

2 个答案:

答案 0 :(得分:0)

我最终这样做了:

fun_1 <- function(...) {
  substitute(list(...))[-1] %>%
    sapply(deparse) %>%
    gsub("~", "", .)
}
foo <- "X"
bar <- "Y"
fun_1(foo, bar)
# [1] "foo" "bar"

fun_2 <- function(...) {
  nonempty <- rlang::dots_splice(...) %>%
    sapply(Negate(rlang::is_empty)) %>%
    which()
  envir <- rlang::caller_env()
  quosures <- rlang::dots_exprs(...) %>%
    lapply(function(x) if (rlang::is_lang(x))
      rlang::lang_tail(x) else x) %>%
    unlist() %>%
    lapply(rlang::new_quosure, env = envir) %>%
    `[`(nonempty)

  rlang::lang("fun_1", !!! quosures)  %>%
    rlang::eval_tidy()
}
fun_2(foo, bar, NULL)
# [1] "foo" "bar"
fun_2(list(foo, bar))
# [1] "foo" "bar"
fun_2(foo, list(bar))
# [1] "foo" "bar"

首先,我必须操纵传递给...的表达式,然后使用正确的环境从中创建quosures,然后构建一个转发到fun_1的调用。

答案 1 :(得分:0)

这是一种更简洁的方法:

library("purrr")
library("rlang")

quo_list <- function(quo) {
  expr <- get_expr(quo)
  if (is_lang(expr, "list")) {
    args <- lang_args(expr)
    map(args, new_quosure, env = get_env(quo))
  } else {
    list(quo)
  }
}

fun2 <- function(...) {
  quos <- quos(..., .ignore_empty = "all")
  quos <- flatten(map(quos, quo_list))
  fun1(!!! quos)
}
fun1 <- function(...) {
  map(quos(...), quo_name)
}

然而,以这种方式解析表达式通常是个坏主意。这里实现的列表拼接只有在调用特别提到list()时才会起作用,如果他们使用rlang::ll()或者用户取消引用列表,或者在许多其他情况下,它们将不起作用。由于这不是一个tidyeval实现,你可能应该删除标签吗?