我正在翻译一种旧语言(FOCAL)的代码,该语言使用“()”作为函数参数和向量索引。目标语言(R)也使用“()”作为函数,但使用“[]”作为索引。FOCAL

变量名限制为两个字符,至少在开始时,我假设所有函数名都有 3 个或更多字符(所有内部函数都是如此)。例如,字符串

" ab(c(kk)) ab(c(5)) sin(x) qr(g) exp(cos(qr)) exp(z) exp(cos(qr(3))) cos(y(3))  sin(3)"

应转换为

" ab[c[kk] ]  ab[c[5] ]  sin(x) qr[g]  exp(cos(qr)) exp(z) exp(cos(qr[3] )) cos(y[3] )  sin(3)"  

作为第一步,我首先通过这个正则表达式(R 代码)运行输入来查找数字索引,

str1 <- gsub('([^a-z][a-z]{1,2})[(]([0-9]{1,})[)]' ,'\\1\\[\\2\\] ' , thestr)

接下来我寻找变量名称的索引

str2 <- gsub('([^a-z][a-z]{1,2})[(]([^)]{1,})[)]' ,'\\1\\[\\2\\] ' , str1)

仅当我对第二个正则表达式进行递归时,它才会成功。我希望有一种更简洁的方法,最好是一种不需要重复操作的方法。

3

  • 3
    强制链接:。更严肃地说:“正确解析括号(在所有情况下)”是一项无法仅使用正则表达式完成的任务。寻找解析器(显然是 FOCAL 解析器?),或者也许有人可以向您指出纯正则表达式的替代方案。


    – 


  • 1
    是一个开源焦点编译器,它有一个--no-run选项可以充当解析器。我不确定该选项的输出是什么,但我同意尝试使用正则表达式实现自己的解析器不会有太大帮助,因此,根据任务的复杂性,这可能是一个不错的起点。


    – 


  • @SamR 谢谢——在这种情况下,我这样做纯粹是为了自己的乐趣,所以使用别人的编译器会让人沮丧:-)


    – 


最佳答案
1

下面的前两个替代方案将输入拆分为有效的 R 代码的子字符串,然后使用 R 的 tokenzier 或内部表达式树。第三和第四个替代方案直接使用正则表达式或处理字符串gsubfn。(3)可能是最快的。

1) getParseData不清楚一般情况会是什么,但如果我们修复不平衡的括号,cos(y(3)))那么这将适用于显示的输入。它会在空格处中断输入,然后应用于fix_parens每个组件。它使用标记输入getParseData,然后跟踪括号类型,parens替换跟在一个或两个字符名称及其匹配项后面的括号。utils 包随 R 一起提供,因此无需安装。

library(utils)

fix_parens <- function(x) {
  txt <- getParseData(parse(text = x))$text
  txt <- txt[txt != ""]
  out <- txt
  paren <- c()
  for(i in seq_along(txt)[-1]) {
    if (nchar(txt[i-1]) %in% 1:2 && txt[i] == "(") {
      paren <- c(paren, "]")
      out[i] <- "["
    } else if (txt[i] == "(") {
      paren <- c(paren, ")")
    } else if (txt[i] == ")") {
      out[i] <- tail(paren, 1)
      paren <- head(paren, -1)
    }
  }
  paste0(out, collapse = "")
}
# same as revised version in question
inp <- " ab(c(kk)) ab(c(5)) sin(x) qr(g) exp(cos(qr)) exp(z) exp(cos(qr(3))) cos(y(3))  sin(3)"

s <- strsplit(inp, " +")[[1]]
s <- s[s != ""]
paste(unname(sapply(s, fix_parens)), collapse = " ")
## [1] "ab[c[kk]] ab[c[5]] sin(x) qr[g] exp(cos(qr)) exp(z) exp(cos(qr[3])) cos(y[3]) sin(3)"

2)R 表达式另一种方法是使用带有递归函数的 R 表达式。 s来自(1),

fix_parens2 <- function(x) {
  if (is.character(x)) x <- parse(text = x)[[1]]
  if (is.symbol(x) || is.numeric(x)) return(x)
  n <- nchar(as.character(x[[1]]))
  if (n %in% 1:2) {
    as.call(c(as.name("["), x[[1]], fix_parens2(x[[2]])))
  } else as.call(c(x[[1]], lapply(x[-1], fix_parens2)))
}

paste(unname(sapply(s, fix_parens2)), collapse = " ")
## [1] "ab[c[kk]] ab[c[5]] sin(x) qr[g] exp(cos(qr)) exp(z) exp(cos(qr[3])) cos(y[3]) sin(3)"

3) 正则表达式 这使用递归正则表达式来匹配 1 或 2 个单词名称、括号和内容,并匹配括号根据需要替换最外层实例重复。

fix_parens3 <- function(x) {
  pat <- "(\\b\\w{1,2})\\(((?:[^()]+|(?R))*)\\)"
  prev <- x
  repeat {
    x <- gsub(pat, "\\1[\\2]", x, perl = TRUE)
    if (x == prev) break
    prev <- x
  }
  x
}

fix_parens3(inp)
## [1] " ab[c[kk]] ab[c[5]] sin(x) qr[g] exp(cos(qr)) exp(z) exp(cos(qr[3])) cos(y[3])  sin(3)"

4) gsubfn gsubfn与之类似gsub,只是替换字符串可以是替换 proto 对象,它将匹配输入到方法中fun,并用 的输出替换匹配funpre在字符串的开头运行,以初始化fun调用中使用的任何变量fun。逻辑非常接近 (1),但没有明确的循环。

library(gsubfn)

p <- proto(pre = function(this) this$paren <- c(),
fun = function(this, x) {
  if (nchar(x) %in% 2:3) {
    this$paren <- c(this$paren, "]")
    sub("(", "[", x, fixed = TRUE)
  } else if (nchar(x) > 3) {
    this$paren <- c(this$paren, ")")
    x
  } else {
     ret <- tail(this$paren, 1)
     this$paren <- head(this$paren, -1)
     ret
  }
})

gsubfn("(\\w+\\(|\\))", p, inp)
## [1] " ab[c[kk]] ab[c[5]] sin(x) qr[g] exp(cos(qr)) exp(z) exp(cos(qr[3])) cos(y[3])  sin(3)"

3

  • 这些看起来不错。我修复了多余的括号(提到这一点是为了让未来的读者不会感到困惑)


    – 

  • 添加了替代方案(3)和(4)。


    – 

  • 哇哦。我想我真的刺激了你的好奇心神经元!:-)


    –