问题
Mathematica has an interesting way of incrementally building a list (or more than one list) of results that you calculate at various points of a complicated computation. I'd like to do something similar in R.
In Mathematica, you can collect the list of each argument to each invocation of the Sow function during a computation by wrapping the entire calculation in call to the Reap function. Does R have any equivalent to these functions? Could you perhaps emulate it with environments and the <<-
operator, or would the scoping rules not permit it?
Edit: Here's a contrived example. Suppose I want to produce a sum of cubes, but I want to also to collect the squares of the numbers I used to make the sum of cubes. I know there may be more idiomatic ways of doing this exact calculation, but it represents getting some final answer while collecting various items produced along the way.
reap(sum(sapply(1:100, function(i) { sow(squares = i * i); i * i * i }))
I'd want this to return something that has the sum of cubes plus a named variable "squares" containing the list of squares.
回答1:
I haven't thoroughly tested this, but it seems to work for your simple example. Here we define reap
and sow
reap <- function(...) {
expr <- substitute(...)
REAPENV <- new.env()
parent.env(REAPENV) <- parent.frame()
x <- eval(expr, REAPENV)
c(list(x), as.list(REAPENV))
}
sow <- function(...) {
expr <- substitute(alist(...))[-1]
for( f in rev(sys.frames())) {
if(exists("REAPENV", envir=f)) {
re <- get("REAPENV", envir=f)
if (is.null(names(expr))) {
names(expr) <- if(length(expr)==1) {"sow"} else {letters[1:length(expr)]}
}
stopifnot(all(nchar(names(expr))!=0))
for(n in names(expr)) {
sx <- eval(expr[[n]], parent.frame())
cv <- if(exists(n, envir=re, inherits=FALSE)) {get(n, envir=re)} else {list()}
if(length(cv)>0) {
assign(n, append(cv, sx), envir=re)
} else {
assign(n, sx, envir=re)
}
}
break;
}
}
invisible(NULL)
}
So the reap()
function basically just defines a new environment and calls it's argument within that context. The sow
function takes a named parameters list, and evaluates it's parameters and assigns to the nearest enclosing "reap" environment. Finally, reap()
will return a list with the "natural" return value of expression it was passed as the first element, and then it will add named elements corresponding to the names used during the sow()
calls. So if you run
reap(sum(sapply(1:5, function(i) { sow(squares=i * i); i * i * i; })))
you get
[[1]]
[1] 225
$squares
[1] 1 4 9 16 25
As I mentioned, this seems to work for the simple test case. I'm sure improvements could be made to finding and assigning to the correct working reaping environment. But this might provide a starting point at least should you wish to pursue something like this.
来源:https://stackoverflow.com/questions/25706285/is-there-an-r-equivalent-to-mathematicas-sow-and-reap-functions-for-building-li