【问题标题】:knitr: Create environment to share variables across chunks which are deleted when finishedknitr:创建环境以在完成时删除的块之间共享变量
【发布时间】:2021-06-20 05:51:09
【问题描述】:

我正在开发一个 R 包 (https://github.com/rcst/rmaxima),它为计算机代数系统 Maxima 提供了一个接口。我想包含一个 knitr 引擎,以便它可以直接与 knitr 一起使用。该包具有启动/生成子进程,然后向 Maxima 发送命令并从 Maxima 获取结果的功能。这样,应该可以在不同的块之间传递结果。

接口通过创建接口的 Rcpp 类的新对象来工作。创建对象会产生一个子进程,删除对象会停止该进程。

我希望引擎在每次文档为knit()ed 时启动一个新的子进程,以便结果可重现。我在想我可以创建一个绑定接口对象的额外环境。引擎检查该对象是否存在于该环境中。如果不存在则创建,否则引擎可以直接继续向接口发送代码。当knit() 退出时,它会退出其环境的范围,并且该环境中的所有变量都会被自动删除。这样子进程就不用停止了,因为接口类get的对象被删除了,进程自动停止了。

但我不知道该怎么做。非常感谢任何提示。

【问题讨论】:

    标签: r knitr environment


    【解决方案1】:

    易慧提供答案here

    本质上,(a)在父框架中设置一个临时变量以检查引擎是否正在运行,并且(b)检查块标签列表以确定当前的标签是否是最后一个并因此触发删除和撕裂处理完毕后down:

     last_label <- function(label = knitr::opts_current$get('label')) {
      if (knitr:::child_mode()) return(FALSE)
      labels <- names(knitr::knit_code$get())
      tail(labels, 1) == label
    }
    
    knitr::knit_engines$set(maxima = local({
      mx <- FALSE
      function(options) {
        if (!mx) {
          maxima$startMaxima()
          mx <<- TRUE
        }
        
        ...  # run maxima code
        
        if (last_label(options$label)) {
          maxima$stopMaxima()
          mx <<- FALSE
        }
      }
    }))
    

    【讨论】:

      【解决方案2】:

      为了完整起见,我还提出了一个可行的解决方案,但不太健壮。

      超出范围的对象会在 R 中自动删除。但是,实际删除发生在 R 的垃圾回收 gc() 期间,无法直接控制。因此,要在 knit() 完成时移除对象,需要在 knit() 的环境中创建该对象,这比引擎调用高一些级别。

      原则上,可以通过knit()on.exit() 注册一个进行实际清理的函数,parent.frame(n=...) 可以检索谁的环境。请注意,当注册到 on.exit() 的表达式被调用时,该范围内的所有对象仍然存在。

       maxima.engine <- function(options) { 
          e <- parent.frame(n = sys.parent() - 2)
          if(!exists("mx", envir = e)) {
              message("starting maxima on first chunk")
              assign(x = "mx", value = new(rmaxima:::RMaxima), envir = e)
              assign(x = "execute", value = get("mx", envir = e)$execute, envir = e)
              assign(x = "stopMaxima", value = get("mx", envir = e)$stopMaxima, envir = e)
      
              # quit maxima "on.exit"ing of knit()
              # eval() doesn't work on "special primitive functions
              # do.call() does ... this may break in the future
              # see https://stat.ethz.ch/pipermail/r-devel/2013-November/067874.html
              do.call("on.exit", list(quote(stopMaxima()), 
                          add = TRUE), envir = e)
          }
      
          code <- options$code
          out <- character(0);
          for(i in 1:length(code))
              out <- c(out, eval(call("execute", code[i]), envir = e))
      
          engine_output(options, code, out)
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-07-22
        • 2022-01-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多