简体   繁体   中英

How can I handle whitespace when using `deparse(substitute))` (or an alternative)?

I'm writing some code that translates mathematical function definitions to valid R code. Therefore I use deparse(substitute)) to access those function definitions in order I can alter them to valid R code.


For example I have the function LN(x)^y that should become log(x)^y . I can do this using the first version of my to_r function:

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

This returns expression(log(x)^y) which is what I expect.


I also get function definitions looking like LN("xa")^y . To handle those I can expand my function:

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

This returns expression(log(x_a)^y) which is fine.


However, when my input becomes something like LN("xa")*2^y this fails:

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

Error in parse(text = gsub(" ", "_", gsub("\\"", "", gsub("LN", "log", : :1:9: unexpected input 1: log(x_a)_ ^

The reason is that deparse(substitute(LN("xa")*2^y)) introduces whitespaces around * and afterwards I gsub those whitespaces with underscores which is a problem for parse .


Is there a way to solve this? Maybe an alternative to deparse(substitute)) ?

(To state the obvious: Replacing gsub(" ", "_", x) with gsub(" ", "", x) is not really an option because variable names get unreadable. For example Reason one of Something would become ReasononeofSomething which is far less readable than the attempted Reason_one_of_Something .)

Here's a helper function to replace any character values in an expression with symbols (with spaces replaced with underscores)

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

We can then use that in your translation function

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

note that this version works with the expressions directly. It doesn't do any deparsing/string manipulation. This is generally safer. This works for the examples you provide

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)

If the input is an R call object then it must, of course, conform to R syntax. In that case we can process it using a recursive function that walks through the input and replaces names containing a space or spaces with the same name but with underscore instead of space(s). Also, at the end replaces LN with log. A call object is returned.

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

Apply as.expression to the result if you want an expression instead of call object.

If the input is a character string then we can simply replace LN with log and for any space of spaces having digit or letter on both sides we can replace the space(s) with an underscore. We return a string or an call object depending on the second argument.

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

If you want an expression instead of a call object use as.expression :

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM