Заменить один символ в выражении несколькими значениями - PullRequest
0 голосов
/ 09 февраля 2019

Учитывая произвольное фиксированное выражение, я хочу заменить один символ набором из нескольких значений.Примеры:

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))

Создано в 2019-02-09 по представительный пакет (v0.2.1)

Можно ли найти env такой, что substitute(f(x), env) равно f(1, b = 4, c = d)?

Примечания:

  • Выше важно, чтобы мы начали с f(x).Мы не можем просто написать as.call(c(quote(f), env)).
  • Вариант использования мотивации описан в https://github.com/ropensci/drake/issues/724 и https://github.com/ropensci/drake/issues/726.
  • Мы не можем использовать !!! из аккуратной оценки, потому что вся аккуратная оценкав drake необходимо обрабатывать отдельно.
  • Этот пост заменяет более раннюю , которая была менее ясной.

Ответы [ 2 ]

0 голосов
/ 11 февраля 2019

Вот пример функции сплайсинга

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 никогда не будет работать, потому что вы не просто заменяете один символ одним значением.Вам необходимо изменить количество параметров, передаваемых функции.

0 голосов
/ 09 февраля 2019

Этот ответ неуклюж, но (я думаю) он делает то, что вам нужно;он был вдохновлен (и одна строка взята) этим ответом о переполнении стека на несколько похожий пост:

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, вы, конечно, можете довольно легко изменить его.

...