缓存函数内生成的计算量大的对象的正确方法

Correct approach to caching a computationally expensive object generated within function

背景

在以下场景中,我正在查看以下工作流程:

  1. 顶级运行器函数,在下面的示例中 running_function,调用了一些较小的函数。
  2. 其中一些函数的计算量很大,并且会在同一组参数上重复调用,因为据称顶级脚本会调用运行器函数

例子

没有尝试缓存对象的情况可以总结如下:

工作函数

painful_function <- function(n = 100) {
  matrix(1:n * n, nrow = n)
}

running_function <-
  function(stat_to_do = c("min", "max", "mean", "sum"),
           painful_size = 1e4) {
    stat_to_do <- match.arg(stat_to_do)

    M_pain <- painful_function(n = painful_size)
    do.call(stat_to_do, list(M_pain))

  }

实际工作

# Object M_pain is created inside running_function
running_function(stat_to_do = "min", painful_size = 100)
# I would like to re-use the M_pain object from the previous function
running_function(stat_to_do = "max", painful_size = 100)
# Re-using M_pain again...
running_function(stat_to_do = "mean", painful_size = 100)
# And again ...
running_function(stat_to_do = "sum", painful_size = 100)

期望的结果

我们的想法是不要多次调用 painful_function,因为它生成的对象在每个场景中都是相同的。 running_function 应使用提供的参数进行评估。

方法

我正在考虑使用 mustashe 包:

library("mustashe")
running_function_mstash <-
  function(stat_to_do = c("min", "max", "mean", "sum"),
           painful_size = 1e4) {
    stat_to_do <- match.arg(stat_to_do)

    stash(var = "M_pain",
          code = {
            painful_function(n = painful_size)
          },
          depends_on = "painful_size")
    do.call(stat_to_do, list(M_pain))
}

这个returns下面的错误:

running_function_mstash(stat_to_do = "min", painful_size = 1e6)

Error in make_hash(depends_on, .TargetEnv) : Some dependencies are missing from the environment.

问题

我有兴趣学习以下内容:

  1. 如何进行这项工作,即 running_function 将仅在传递下来的参数之一发生变化时执行 painful_function,否则结果对象存储在文件中
  2. 有什么更好的使用方法。一种微不足道的“蛮力”方法是创建一个临时的 RDS,名称古怪,并且仅在文件不存在时才执行 painful_function。这种蹩脚的做法有明显的弊端。我想找到一个强大的解决方案,涵盖类似的、可行的场景。

可能是对象没有被检测到。根据?stash中的例子,我们需要使用<<-

running_function_mstash <-
  function(stat_to_do = c("min", "max", "mean", "sum"),
           painful_size = 1e4) {
    stat_to_do <- match.arg(stat_to_do)
    painful_size <<- painful_size
    stash(var = "M_pain",
          code = {
            painful_function(n = painful_size)
          },
          depends_on = "painful_size")
    do.call(stat_to_do, list(M_pain))
}

running_function_mstash(stat_to_do = "min", painful_size = 1e6)
#Stashing object.
#[1] 1e+06