将C代码转换为R代码:解析以更改R中的C函数(pow(a,b)到a ^ b)

时间:2017-07-31 21:31:27

标签: c r wolfram-mathematica

我使用Mathematica生成等式作为C代码(使用CForm []),以便将等式导出为字符串并在R中使用它。

例如,作为字符串导入R的CForm []输出如下所示:

"Tau * Power(Omega * (-(R * Gamma) + R),(Tau + R))"

我的问题是如何最好地将上面的C代码转换为这样的R表达式:

Tau * (Omega * (-(R * Gamma) + R ))^(Tau + R)

根据之前一篇关于将Mathematica代码转换为R代码(Convert Mathematica equations into R code)的帖子的建议,我意识到合理的做法是将Power()重新定义为函数,即:

Power <- function(a,b) {a^b}

但是,通过一系列测试,我发现以下列形式评估一个表达式:

eval(parse(text="Tau * (Omega * (-(R * Gamma) + R ))^(Tau + R)"))

比将Power()定义为函数并评估以下内容更快(在我的mac上快4倍):

eval(parse(text="Tau * Power(Omega * (-(R * Gamma) + R),(Tau + R))"))

这似乎是一个复杂的模式匹配问题,但我找不到任何解决方案。我很感激任何建议。

1 个答案:

答案 0 :(得分:2)

这里有多个问题:

  1. 您的等式不是标准C代码。来自Mathematica的CForm[]并未将您的代码转换为正确的C语法。也许您可以关注this answer并使用SymbolicC来解决此问题
  2. 你的问题更多的是从语言A解析为语言B.正如@Olaf在评论中所提到的:您可能更好地使用真正的C函数并从R调用它或手动转换它,具体取决于你经常这样做
  3. 但是,根据您的要求(如果我理解您想要实现的目标)和教育目的;这是一个例子,我们将使用R转换你的&#34;伪C&#34;字符串并创建内联cfunction()

    注意:这绝不是优雅或实用的,但总体思路应该有助于您入门

    假设以下等式:

    v1 <- "4 * Power(Omega * (-(R * Gamma) + R),(Tau + R))"
    

    从原始字符串中提取所有变量和函数

    n1 <- stringi::stri_extract_all_words(v1)[[1]]
    

    创建&#34;函数的命名向量以重新编码&#34; (以及没有它们且没有数字的子集)

    newFunc <- c("Power" = "pow")   
    n2 <- setdiff(n1, names(newFunc))
    n3 <- n2[is.na(as.numeric(n2))]
    

    构建替换列表以提供gsubfn()。为了这个例子,我们用新的函数替换旧函数并在变量周围包裹asReal()

    toreplace <- setNames(
      as.list(c(newFunc, paste0("asReal(", n3, ")"))), 
      c(names(newFunc), n3)
    )
    
    v2 <- gsubfn::gsubfn(paste(names(toreplace), collapse = "|"), toreplace, v1)
    

    然后,您可以将此新字符串传递给cfunction()以在R

    中执行
    #install.packages("inline")
    library(inline)
    foo <- cfunction(
      sig  = setNames(rep("integer", length(n3)), n3), 
      body = paste0(
        "SEXP result = PROTECT(allocVector(REALSXP, 1));
         REAL(result)[0] = ", v2, "; 
         UNPROTECT(1);
         return result;"
      )
    )
    

    这比使用eval(parse("...")) ^或定义Power()函数

    更快
    Tau = 21; Omega = 22; R = 42; Gamma = 34
    Power <- function(x,y) {x^y}
    
    microbenchmark::microbenchmark(
      C  = foo(Omega, R, Gamma, Tau),
      R1 = eval(parse(text="4 * ((Omega * (-(R * Gamma) + R ))^(Tau + R))")),
      R2 = eval(parse(text="4 * Power(Omega * (-(R * Gamma) + R),(Tau + R))")), 
      times = 10L
    )
    
    #Unit: microseconds
    # expr     min      lq     mean   median      uq      max neval
    #    C   1.233   2.194   5.9555   2.9955   3.302   34.194    10
    #   R1 190.012 202.781 230.5187 218.1035 243.891  337.209    10
    #   R2 189.162 191.798 374.5778 207.6875 225.078 1868.746    10