Как я могу обрабатывать пробелы при использовании `deparse (замена))` (или альтернативы)?

#r

#r

Вопрос:

Я пишу некоторый код, который переводит определения математических функций в допустимый R-код. Поэтому я использую deparse(substitute)) для доступа к этим определениям функций, чтобы я мог изменить их на действительный R-код.


Например, у меня есть функция, LN(x)^y которая должна стать log(x)^y . Я могу сделать это, используя первую версию моей to_r функции:

 to_r <- function(x) {
  parse(text = gsub("LN", "log", deparse(substitute(x))))
}
to_r(LN(x)^y)
  

Это возвращает expression(log(x)^y) то, что я ожидаю.


Я также получаю определения функций, выглядящие как LN("x a")^y . Чтобы обрабатывать их, я могу расширить свою функцию:

 to_r_2 <- function(x) {
  parse(text = gsub(" ", "_", gsub(""", "", gsub("LN", "log", deparse(substitute(x))))))
}
to_r_2(LN("x a")^y)
  

Это возвращает expression(log(x_a)^y) , что нормально.


Однако, когда мой ввод становится чем-то вроде LN("x a")*2^y этого, происходит сбой:

 parse(text = gsub(" ", "_", gsub(""", "", gsub("LN", "log", deparse(substitute(LN("x a")*2^y))))))
  

Ошибка при синтаксическом анализе (text = gsub(«», «_», gsub(«»», «»», gsub («LN», «log»,
: :1:9: неожиданный ввод 1: log (x_a)_
^

Причина в том, что deparse(substitute(LN("x a")*2^y)) вводятся пробелы вокруг * , а затем I gsub эти пробелы с подчеркиванием, что является проблемой для parse .


Есть ли способ решить эту проблему? Может быть, альтернатива deparse(substitute)) ?

(Констатируя очевидное: замена gsub(" ", "_", x) на gsub(" ", "", x) на самом деле не вариант, потому что имена переменных становятся нечитаемыми. Например, Reason one of Something стало бы ReasononeofSomething гораздо менее читаемым, чем предпринятое Reason_one_of_Something .)

Комментарии:

1. Я здесь немного запутался. Можете ли вы четко определить, что такое необработанный ввод (строка? выражение, заключенное в кавычки? обещание?) и каков желаемый результат для этого ввода. Возможно, разделите ваш код на функцию, чтобы мы могли четко видеть, какие части мы можем изменить. Я не уверен точно, каковы ваши правила для синтаксического анализа недопустимых операторов.

2. @MrFlick: Идея состоит в том, чтобы поместить это внутри функции, чтобы иметь вызовы типа: to_r(LN(x)^y) , смотрите Мою правку.

3. Но вы никогда не сможете вызвать to_r_2(LN(x a)^y) . Если вы передаете выражение, оно должно использовать допустимый синтаксис R. Откуда вы знаете, действительно ли они пытаются передать строку в вашу функцию? Вы хотите заменить каждую строку символом?

4. @MrFlick: Вы правы. to_r_2(LN(x a)^y никогда не будет работать. Я удалил этот регистр. Есть ли еще способ обработать to_r_2(LN("x a")*2^y случай?

5. Я думал, что gsub был бы идеальным вариантом использования для rapply но, к сожалению, он не обрабатывает вызовы как списки… Хотя это было бы неплохо… rapply(x,function(x) as.symbol(gsub(" ","_",x)), "character", how = "replace")

Ответ №1:

Вот вспомогательная функция для замены любых символьных значений в выражении символами (с заменой пробелов символами подчеркивания)

 chr_to_sym <- function(x) {
  if (is(x, "call")) {
    as.call(do.call("c",lapply(as.list(x), chr_to_sym), quote=T))
  } else if (is(x, "character")) {
    as.symbol(gsub(" ","_", x))
  } else {
    x
  }
}
  

Затем мы можем использовать это в вашей функции перевода

 to_r <- function(x) {
  expr <- substitute(x)
  expr <- do.call("substitute", list(expr, list(LN=quote(log))))
  as.expression(chr_to_sym(expr))
}
  

обратите внимание, что эта версия работает с выражениями напрямую. Он не выполняет никаких операций с удалением / строкой. Это, как правило, безопаснее. Это работает для примеров, которые вы предоставляете

 to_r(LN(x)^y)
# expression(log(x)^y)
to_r(LN("x a")^y)
# expression(log(x_a)^y)
to_r(LN("x a")*2^y)
# expression(log(x_a) * 2^y)
  

Ответ №2:

Если ввод является объектом вызова R, то он, конечно, должен соответствовать синтаксису R. В этом случае мы можем обработать это с помощью рекурсивной функции, которая просматривает входные данные и заменяет имена, содержащие пробел или пробелы, на те же имена, но с подчеркиванием вместо пробелов. Кроме того, в конце заменяет LN на log . Возвращается объект вызова.

 rmSpace <- function(e) {
    if (length(e) == 1) e <- as.name(gsub(" ", "_", as.character(e)))
      else for (i in 1:length(e)) e[[i]] <- Recall(e[[i]])
    do.call("substitute", list(e, list(LN = as.name("log"))))
}
rmSpace(quote(LN("x a")*2^y))
## log(x_a) * `2`^y

# to input an expression add [[1]] after it to make it a call object
rmSpace(expression(LN("x a")*2^y)[[1]])
## log(x_a) * `2`^y
  

Примените as.expression к результату, если вы хотите выражение вместо объекта вызова.

Если входные данные представляют собой символьную строку, то мы можем просто заменить LN на log , а для любого пробела, имеющего цифру или букву с обеих сторон, мы можем заменить пробелы символом подчеркивания. Мы возвращаем строку или вызываемый объект в зависимости от второго аргумента.

 rmSpace2 <- function(s, retclass = c("character", "call")) {
  s1 <- gsub("\bLN\b", "log", s)
  s2 <- gsub("([[:alnum:]])  ([[:alnum:]])", "\1_\2", s1, perl = TRUE)
  retclass <- match.arg(retclass)
  if (retclass == "character") s2 else parse(text = s2)[[1]]
}
rmSpace2("LN(x a)*2^y")
## [1] "log(x_a)*2^y"

rmSpace2("LN(x a)*2^y", "call")
## log(x_a) * 2^y
  

Если вам нужно выражение вместо объекта вызова, используйте as.expression :

 as.expression(rmSpace2("LN(x a)*2^y", "call"))
## expression(log(x_a) * 2^y)