用多个值替换表达式中的一个符号

时间:2019-02-09 17:52:39

标签: r metaprogramming static-code-analysis

给出一个任意的固定表达式,我想用多个值的集合代替单个符号。例子:

Expression       | Symbol | Replace with               | Desired Output
-----------------------------------------------------------------------------------------
f(x, 5)          | x      | a = 1, b = sym, c = "char" | f(a = 1, b = sym, c = "char", 5)
g(f(h(y)), z)    | y      | 1, 2, 3                    | g(f(h(1, 2, 3)), z)
g(f(h(y), z), z) | z      | 4, x                       | g(f(h(y), 4, x), 4, x)

substitute()函数非常接近,但这并不是我想要的。在下面的示例中,我想将f(x)转换为f(1, b = 4, c = d),但尚未找到正确的env参数。

substitute(
  expr = f(x),
  env = list(x = list(1, b = 4, c = rlang::sym("d")))
)
#> f(list(1, b = 4, c = d))

substitute(
  expr = f(x),
  env = list(x = as.call(c(quote(x), 1, b = 4, c = quote(d)))[-1])
)
#> f(1(b = 4, c = d))

reprex package(v0.2.1)于2019-02-09创建

是否可以找到一个env等于substitute(f(x), env)的{​​{1}}?

备注:

2 个答案:

答案 0 :(得分:1)

这是一个拼接功能

splice <- function(x, replacements) {
  if (is(x, "call")) {
    as.call(do.call("c",lapply(as.list(x), splice, replacements), quote=T))
  } else if (is(x, "name")) {
    if (deparse(x) %in% names(replacements)) {
      return(replacements[[deparse(x)]])
    } else {
      list(x)
    }
  } else {
    list(x)
  }
}

似乎可以与示例输入一起使用

splice(quote(f(x, 5) ), list(x=list(a = 1, b = quote(sym), c = "char" )))
# f(a = 1, b = sym, c = "char", 5)
splice(quote(g(f(h(y)), z)) , list(y=list(1,2,3)))
# g(f(h(1, 2, 3)), z)
splice(quote(g(f(h(y), z), z)), list(z=list(4, quote(x))) )
# g(f(h(y), 4, x), 4, x)

基本上,您只是交换符号名称。它也应该适用于不在列表中的单个变量替换。

splice(quote(f(x,5)), list(x=7))
# f(7, 5)

基本上,您需要通过将其处理为列表来重新编写呼叫。这是tidyverse函数在后台执行的操作。他们拦截当前呼叫,将其重写,然后评估新扩展的呼叫。 substitute永远不会起作用,因为您不只是将一个符号替换为一个值。您需要更改传递给函数的参数数量。

答案 1 :(得分:0)

这个答案很笨拙,但是(我认为)它可以满足您的需求;它是受this Stack Overflow answer启发(并从其中摘录的)写到了一篇相关的文章:

multi_substitute <- function(expr, key, ...) {
    expr <- deparse(substitute(expr))
    key <- deparse(substitute(key))
    # The following line is the bit I got from the mentioned SO answer
    l <- sapply( substitute(list(...)), deparse)[-1] 
    l <- paste(names(l), l, sep = " = ")
    l <- sub("^ = ", "", l)
    l <- paste(l, collapse = ",")
    vals <- deparse(substitute(...))
    result <- sub(key, l, expr)
    return(parse(text = result)[[1]])
}
multi_substitute(f(x), x, 1, b = 4, c = quote(d))
# f(1, b = 4, c = quote(d))

您也许可以使它更加优雅,或对其进行定制以更好地满足您的需求。

如果您不想在那里强制提供key参数,那么您当然可以很容易地更改它。