当使用`deparse(substitute)`(或其他替代方法)时,我该如何处理空格?

4

我正在编写一些代码,将数学函数定义转换为有效的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)

这将返回表达式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))))))

出现错误是因为deparse(substitute(LN("x a")*2^y))*周围引入了空格,然后我用gsub将这些空格替换为下划线,这对于parse来说是个问题。


有没有解决方法?也许有deparse(substitute)的替代方案?

(显而易见的是:用gsub(" ", "", x)替换gsub(" ", "_", x)不是一个真正的选择,因为变量名会变得难以阅读。例如, Something 的原因之一将变成Something的原因之一,这比尝试的Something_原因_之_一更难以阅读。)


我有点困惑。你能清楚地确定原始输入是什么(字符串?引用表达式?承诺?)以及该输入的期望输出是什么吗?也许将你的代码分成一个函数,这样我们就可以清楚地看到哪些部分可以更改。我不确定你解析无效语句的规则是什么。 - MrFlick
@MrFlick:这个想法是将它放在一个函数内,以便进行像to_r(LN(x)^y)这样的调用,请参见我的编辑。 - symbolrush
1
但是你永远无法调用 to_r_2(LN(x a)^y)。如果您正在传递一个表达式,它需要使用有效的 R 语法。您如何知道他们实际上是在尝试将字符串传递给您的函数?您想要用符号替换每个字符串吗? - MrFlick
@MrFlick: 您是正确的。 to_r_2(LN(x a)^y 将永远不起作用。我已将其删除。仍然有办法处理 to_r_2(LN("x a")*2^y 案例吗? - symbolrush
我认为gsub是rapply的一个完美用例,但不幸的是它不能将调用视为列表...虽然这本来很好... rapply(x,function(x) as.symbol(gsub(" ","_",x)), "character", how = "replace") - moodymudskipper
2个回答

6
这里有一个帮助函数,可以将表达式中的任何字符值替换为符号(空格替换为下划线)。
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,对于两侧都有数字或字母的空格,我们可以将空格替换为下划线。根据第二个参数,我们返回一个字符串或一个调用对象。

最初的回答:

如果你想得到一个表达式而不是一个函数调用对象,那么需要对结果应用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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接