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

Replace one symbol in an expression with multiple values

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

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) 等于 f(1, b = 4, c = d)

备注:

这个答案很笨拙,但(我认为)它可以满足您的需求;它受到 的启发(以及从中摘录的一行)到某种相关的 post:

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 参数,您当然可以很容易地改变它。

这里有一个拼接函数

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 永远不会起作用,因为您不仅仅是用一个值替换一个符号。您需要更改传递给函数的参数数量。