> On Feb 24, 2015, at 10:50 AM, <luke-tierney at uiowa.edu> wrote: > > The documentation is not specific enough on the indented semantics in > this situation to consider this a bug. The original R-level > implementation of lapply was > > lapply <- function(X, FUN, ...) { > FUN <- match.fun(FUN) > if (!is.list(X)) > X <- as.list(X) > rval <- vector("list", length(X)) > for(i in seq(along = X)) > rval[i] <- list(FUN(X[[i]], ...)) > names(rval) <- names(X) # keep `names' ! > return(rval) > } > > and the current internal implementation is consistent with this. With > a loop like this lazy evaluation and binding assignment interact in > this way; the force() function was introduced to help with this. > > That said, the expression FUN(X[[i]], ...) could be replaced by > > local({ > i <- i > list(FUN(X[[i]], ...) > }) > > which would produce the more desirable result > > > sapply(test, function(myfn) myfn(2)) > [1] 2 4 6 8 >Would the same semantics be applied to parallel::mclapply and friends? sapply(lapply(1:4, function(c){function(i){c*i}}), function(f) f(2)) # [1] 8 8 8 8 sapply(mclapply(1:4, function(c){function(i){c*i}}), function(f) f(2)) # [1] 6 8 6 8 I understand why they differ, but making mclapply easier for 'drop-in' parallelism might be a good thing. Michael
Actually, it depends on the number of cores:
> fun1 <- function(c){function(i){c*i}}
> fun2 <- function(f) f(2)
> sapply(mclapply(1:4, fun1, mc.cores=1L), fun2)
[1] 8 8 8 8
> sapply(mclapply(1:4, fun1, mc.cores=2L), fun2)
[1] 6 8 6 8
> sapply(mclapply(1:4, fun1, mc.cores=4L), fun2)
[1] 2 4 6 8
> >/ On Feb 24, 2015, at 10:50 AM, <luke-tierney at uiowa.edu
<https://stat.ethz.ch/mailman/listinfo/r-devel>> wrote:
> />/
> />/ The documentation is not specific enough on the indented semantics
in
> />/ this situation to consider this a bug. The original R-level
> />/ implementation of lapply was
> />/
> />/ lapply <- function(X, FUN, ...) {
> />/ FUN <- match.fun(FUN)
> />/ if (!is.list(X))
> />/ X <- as.list(X)
> />/ rval <- vector("list", length(X))
> />/ for(i in seq(along = X))
> />/ rval[i] <- list(FUN(X[[i]], ...))
> />/ names(rval) <- names(X) # keep `names' !
> />/ return(rval)
> />/ }
> />/
> />/ and the current internal implementation is consistent with this.
With
> />/ a loop like this lazy evaluation and binding assignment interact in
> />/ this way; the force() function was introduced to help with this.
> />/
> />/ That said, the expression FUN(X[[i]], ...) could be replaced by
> />/
> />/ local({
> />/ i <- i
> />/ list(FUN(X[[i]], ...)
> />/ })
> />/
> />/ which would produce the more desirable result
> />/
> />/ > sapply(test, function(myfn) myfn(2))
> />/ [1] 2 4 6 8
> />/
> /
> Would the same semantics be applied to parallel::mclapply and friends?
>
> sapply(lapply(1:4, function(c){function(i){c*i}}), function(f) f(2))
>
> # [1] 8 8 8 8
>
> sapply(mclapply(1:4, function(c){function(i){c*i}}), function(f) f(2))
>
> # [1] 6 8 6 8
>
> I understand why they differ, but making mclapply easier for
'drop-in' parallelism might be a good thing.
>
> Michael
> On Feb 25, 2015, at 5:35 PM, Benjamin Tyner <btyner at gmail.com> wrote: > > Actually, it depends on the number of cores:Under current semantics, yes. Each 'stream' of function calls is lazily capturing the last value of `i` on that core. Under Luke's proposed semantics (IIUC), the result would be the same (2,4,6,8) for both parallel and serial execution. This is what allows for 'drop-in' parallelism.>> fun1 <- function(c){function(i){c*i}} >> fun2 <- function(f) f(2) >> sapply(mclapply(1:4, fun1, mc.cores=1L), fun2) > [1] 8 8 8 8 >> sapply(mclapply(1:4, fun1, mc.cores=2L), fun2) > [1] 6 8 6 8 >> sapply(mclapply(1:4, fun1, mc.cores=4L), fun2) > [1] 2 4 6 8 > >>> / On Feb 24, 2015, at 10:50 AM, <luke-tierney at uiowa.edu <https://stat.ethz.ch/mailman/listinfo/r-devel>> wrote: >> />/ >> />/ The documentation is not specific enough on the indented semantics in >> />/ this situation to consider this a bug. The original R-level >> />/ implementation of lapply was >> />/ >> />/ lapply <- function(X, FUN, ...) { >> />/ FUN <- match.fun(FUN) >> />/ if (!is.list(X)) >> />/ X <- as.list(X) >> />/ rval <- vector("list", length(X)) >> />/ for(i in seq(along = X)) >> />/ rval[i] <- list(FUN(X[[i]], ...)) >> />/ names(rval) <- names(X) # keep `names' ! >> />/ return(rval) >> />/ } >> />/ >> />/ and the current internal implementation is consistent with this. With >> />/ a loop like this lazy evaluation and binding assignment interact in >> />/ this way; the force() function was introduced to help with this. >> />/ >> />/ That said, the expression FUN(X[[i]], ...) could be replaced by >> />/ >> />/ local({ >> />/ i <- i >> />/ list(FUN(X[[i]], ...) >> />/ }) >> />/ >> />/ which would produce the more desirable result >> />/ >> />/ > sapply(test, function(myfn) myfn(2)) >> />/ [1] 2 4 6 8 >> />/ >> / >> Would the same semantics be applied to parallel::mclapply and friends? >> >> sapply(lapply(1:4, function(c){function(i){c*i}}), function(f) f(2)) >> >> # [1] 8 8 8 8 >> >> sapply(mclapply(1:4, function(c){function(i){c*i}}), function(f) f(2)) >> >> # [1] 6 8 6 8 >> >> I understand why they differ, but making mclapply easier for 'drop-in' parallelism might be a good thing. >> >> Michael > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel