Is there an R equivalent to Mathematica's Sow and Reap functions for building lists?

半腔热情 提交于 2019-12-24 12:24:08

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!