【问题标题】:Replace one symbol in an expression with multiple values用多个值替换表达式中的一个符号
【发布时间】:2019-07-03 16:08:44
【问题描述】:

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

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 年 2 月 9 日创建

是否有可能找到一个env 使得substitute(f(x), env) 等于f(1, b = 4, c = d)

备注:

【问题讨论】:

    标签: r metaprogramming static-code-analysis


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

    【讨论】:

    • 非常感谢@MrFlick!很高兴我现在知道do.call(quote = TRUE)
    【解决方案2】:

    这个答案很笨拙,但是(我认为)它可以满足您的需求;它的灵感来自(并取自)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 参数,您当然可以很容易地改变它。

    【讨论】:

    • 感谢您的尝试,但我宁愿避免文本替换。 Deparsing 和 parsing 很慢,文本替换有边缘情况。例如,multi_substitute(f(not_x, x), x, 1, b = 4, c = quote(d)) 等于 f(not_1, b = 4, c = quote(d), x),而不是所需的 f(not_x, 1, b = 4, c = quote(d))
    猜你喜欢
    • 1970-01-01
    • 2016-01-01
    • 2015-10-11
    • 2015-01-28
    • 1970-01-01
    • 2015-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多