更糟的罪过:副作用或者通过大量物体?
我在函数内的循环内有一个函数。内部函数获取并存储内存中的大量数据(作为全局变量……我使用的是" R",就像" S-Plus"一样)。循环遍历要获取的一长串数据。外部函数将启动该过程,并传入要获取的数据集列表。
for (dataset in list_of_datasets) { for (datachunk in dataset) { <process datachunk> <store result? as vector? where?> } }
我对内部函数进行了编程,以便在移至下一个之前存储每个数据集,因此外部函数的所有工作都是对全局变量产生副作用的。这比收集并返回一个巨大的,耗费内存的向量是好还是坏?有没有更好的第三种方法?
如果我将数据向量存储在数据库而不是内存中,答案是否会改变?理想情况下,我希望能够终止该功能(或者由于网络超时而使其失败),而不会丢失终止前已处理的所有信息。
解决方案
回答
在不知道所使用的语言/编译器的情况下很难明确地说出来。但是,如果我们可以简单地将指针/引用传递给正在创建的对象,则对象本身的大小与函数调用的速度无关。将来处理这些数据可能是另一回事。
回答
在外部函数中使用变量而不是全局变量。这可以使我们获得这两种方法的优点:我们不会改变全局状态,也不会复制大量数据。如果我们必须提早退出,则只返回部分结果。
(请参阅R手册中的"范围"部分:http://cran.r-project.org/doc/manuals/R-intro.html#Scope)
回答
第三种方法:内部函数返回对大数组的引用,然后循环内的下一条语句将取消引用并在需要的地方存储(理想情况下,使用单个指针存储,而不必通过复制整个数组)。
这消除了副作用和大型数据结构的传递。
回答
这不会对内存使用产生太大影响,因此我们最好将代码清理干净。
由于R对变量具有"修改时复制"功能,因此修改全局对象与在返回值中传递某些内容具有相同的内存含义。
如果将输出存储在数据库(甚至文件)中,则不会出现内存使用问题,并且数据在创建时将逐渐可用,而不仅仅是在最后。使用数据库的速度是否更快主要取决于我们使用的内存量:减少的是垃圾回收将用来支付写入磁盘的费用。
R中既有时间分析器,也有内存分析器,因此我们可以凭经验看到影响。
回答
我不确定我是否理解这个问题,但是我有几个解决方案。
- 在函数内部,创建向量列表并返回。
- 在函数内部,创建一个环境并将其存储在其中的所有向量。只要确保在出现错误的情况下返回环境即可。
在R中:
help(environment) # You might do something like this: outer <- function(datasets) { # create the return environment ret.env <- new.env() for(set in dataset) { tmp <- inner(set) # check for errors however you like here. You might have inner return a list, and # have the list contain an error component assign(set, tmp, envir=ret.env) } return(ret.env) } #The inner function might be defined like this inner <- function(dataset) { # I don't know what you are doing here, but lets pretend you are reading a data file # that is named by dataset filedata <- read.table(dataset, header=T) return(filedata) }
莱夫
回答
记住你的Knuth。 "过早的优化是所有编程弊端的根源。"
尝试免费的副作用。查看它是否满足绩效目标。如果可以,那么很好,我们首先没有问题;如果不是,则使用副作用,并为下一个程序员记录手被迫。
回答
仅供参考,这是一个完整的示例玩具解决方案,可以避免副作用:
outerfunc <- function(names) { templist <- list() for (aname in names) { templist[[aname]] <- innerfunc(aname) } templist } innerfunc <- function(aname) { retval <- NULL if ("one" %in% aname) retval <- c(1) if ("two" %in% aname) retval <- c(1,2) if ("three" %in% aname) retval <- c(1,2,3) retval } names <- c("one","two","three") name_vals <- outerfunc(names) for (name in names) assign(name, name_vals[[name]])