能否对嵌套函数使用memoise?R语言代码场景咨询
我按照建议用purrr::map而非Vectorize实现了矢量化的数据加载函数,代码结构如下:
load_data <- function(key){ load_data_one <- function(key){ ... # 根据key获取数据的逻辑 ... data } # 支持传入key向量,返回合并后的结果 result <- map_dfr(key, load_data_one) }
由于数据检索成本很高,我想了解memoise包在此场景下的最佳使用方式,具体疑问如下:
- 理想方案是给嵌套的
load_data_one加缓存,这样调用load_data处理向量时只会计算未缓存过的key,这个思路是否正确? - 如果转而给
load_data加缓存,当传入的key向量和之前略有不同时,memoise能不能只计算新增的元素? - 给嵌套的
load_data_one用memoise有没有潜在问题?是否需要把它移到load_data外面?
1. 给嵌套的load_data_one加缓存是最优选择
这个思路完全正确。因为load_data_one是针对单个key的逻辑,给它加memoise后,每次调用load_data处理向量时,每个key都会先检查缓存:如果之前已经加载过这个key的数据,直接从缓存取;只有未缓存的key才会执行实际的检索逻辑,能最大化减少重复的高成本检索,完美匹配你“仅计算未知结果”的需求。
2. 给load_data加缓存无法实现“仅计算新元素”
memoise是基于整个函数的输入参数来缓存结果的。如果两次传入的key向量哪怕只有一个元素不同,memoise都会认为是全新的输入,会重新执行整个load_data函数——也就是重新遍历所有key并执行检索,完全不会复用之前缓存过的单个key的结果。这种方式缓存粒度太粗,达不到你想要的效果,不推荐。
3. 嵌套的load_data_one使用memoise的潜在问题与解决
直接在load_data内部给load_data_one加memoise会有缓存失效的问题:每次调用load_data时,都会重新定义load_data_one这个函数,而memoise是绑定在函数对象上的。这意味着每次调用load_data,load_data_one都是一个新的函数实例,之前的缓存会丢失,相当于每次都要重新缓存所有key,完全起不到复用的作用。
解决方法很简单:把load_data_one移到load_data外部,然后给它加memoise。修改后的代码结构如下:
# 先定义单个key的加载函数并缓存 load_data_one <- memoise::memoise(function(key){ ... # 根据key获取数据的逻辑 ... data }) # 矢量化的入口函数 load_data <- function(key){ result <- purrr::map_dfr(key, load_data_one) }
这样load_data_one是一个全局的函数实例,memoise的缓存会一直保留,每次调用load_data时都能复用之前的缓存结果。
内容的提问来源于stack exchange,提问作者Hauke L.

