我想使用memoization来缓存某些昂贵操作的结果,这样就不会一遍又一遍地计算它们。
memoise和R.cache都符合我的需求。但是,我发现调用之间的缓存效果不佳。
以下示例演示了我所看到的问题:
library(memoise)
# Memoisation works: b() is called only once
a <- function(x) runif(1)
replicate(5, a())
b <- memoise(a)
replicate(5, b())
# Memoisation fails: mfn() is called every single time
ProtoTester <- proto(
calc = function(.) {
fn <- function() print(runif(1))
mfn <- memoise(fn)
invisible(mfn())
}
)
replicate(5, ProtoTester$calc())
根据回答更新
根据是使用持久缓存还是非持久缓存,此问题可以有不同的答案。非持久性缓存(例如memoise
)可能需要单个分配,然后下面的答案是一个很好的方法。持久性缓存(例如R.cache
)在会话中起作用,对于多个分配应该健壮。上述方法适用于R.cache
。尽管进行了多次分配,但fn
仅在R.cache
时调用一次。它会被memoise
调用两次。
> ProtoTester <- proto(
+ calc = function(.) {
+ fn <- function() print(runif(1))
+ invisible(memoizedCall(fn))
+ }
+ )
> replicate(5, ProtoTester$calc())
[1] 0.977563
[1] 0.1279641
[1] 0.01358866
[1] 0.9993092
[1] 0.3114813
[1] 0.97756303 0.12796408 0.01358866 0.99930922 0.31148128
> ProtoTester <- proto(
+ calc = function(.) {
+ fn <- function() print(runif(1))
+ invisible(memoizedCall(fn))
+ }
+ )
> replicate(5, ProtoTester$calc())
[1] 0.97756303 0.12796408 0.01358866 0.99930922 0.31148128
我认为我遇到R.cache
问题的原因是我将proto
方法作为函数传递给memoizedCall
。 proto
方法以R.cache
很难处理的方式绑定到环境中。在这种情况下,您需要做的是取消绑定函数(从实例化方法获取到简单函数),然后手动传递对象作为第一个参数。以下示例显示了其工作原理(Report
和Report$loader
都是proto
个对象:
# This will not memoize the call
memoizedCall(Report$loader$download_report)
# This works as intended
memoizedCall(with(Report$loader, download_report), Report$loader)
我很想知道为什么R.cache
适用于绑定到环境但通过proto
实例化方法失败的普通函数。
答案 0 :(得分:4)
在您的代码中,每次调用该函数时都会重新记忆该函数。 以下内容应该有效:只有在定义时才会被记忆一次。
ProtoTester <- proto(
calc = {
fn <- function() print(runif(1))
mfn <- memoise(fn)
function(.) mfn()
}
)
replicate(5, ProtoTester$calc())
答案 1 :(得分:3)
另一种解决方案是使用evals
进行评估来自(我的)pander package,其中内部(临时在当前R会话的环境中或持久存储磁盘存储)缓存引擎。基于代码的简短示例:
library(pander)
ProtoTester <- proto(
calc = function(.) {
fn <- function() runif(1)
mfn <- evals('fn()')[[1]]$result
invisible(mfn)
}
)
关闭并打开缓存后运行evals
会导致:
> evals.option('cache', FALSE)
> replicate(5, ProtoTester$calc())
[1] 0.7152186 0.4529955 0.4160411 0.1166872 0.8776698
> evals.option('cache', TRUE)
> evals.option('cache.time', 0)
> replicate(5, ProtoTester$calc())
[1] 0.7716874 0.7716874 0.7716874 0.7716874 0.7716874
请注意,evals.option
函数将很快重命名为evalsOption
,以缓解有关S3方法的R CMD check
警告。