4

This answer brought up the question of how the ellipsis feature in R handles empty arguments. Apparently an empty argument in ... works sometimes (see lapply version below) but not other times (see sapply version). Here's the example:

lst <- list(x=matrix(1))
lapply(lst, "[", 1, )
# $x
# [1] 1
sapply(lst, "[", 1, )
# Error in lapply(X = X, FUN = FUN, ...) : 
#   argument is missing, with no default

From what I can tell, sapply actually just reuses its ... arguments when calling lapply. So I don't understand why lapply works but sapply doesn't. Can anybody explain this behavior.

In the sapply help it states that

sapply(*, simplify = FALSE, USE.NAMES = FALSE) is equivalent to lapply(*).

However, I get the same results as above for the following:

lapply(lst, "[", i=1, j=)
sapply(lst, "[", i=1, j=, simplify=FALSE, USE.NAMES=FALSE)

By the way, I know that just adding TRUE would solve the issue in this case, but I'm more interested in why there is a difference, not how to solve it. I'm actually more surprised that it works for the lapply case than that it doesn't for the sapply one.

Community
  • 1
  • 1
shadow
  • 21,823
  • 4
  • 63
  • 77
  • 1
    I guess it's not specific to `sapply` but to any `lapply` wrapper: `(function(X, FUN, ...) lapply(X, FUN, ...))(lst, "[", 1, )`; Perhaps, it has to do with the "historical reasons" Note in `?lapply` while its wrappers have to evaluate their arguments? – alexis_laz Aug 07 '14 at 10:22
  • have you looked at the source code for sapply and lapply? – James Tobin Aug 11 '14 at 14:58
  • @JamesTobin: I have looked at the R part of the source code. `sapply` really just calls `lapply` (at which point it fails) and then simplifies the results. `lapply` calls `.Internal(lapply(X, FUN))`, and I haven't looked at the source code of that. `lapply` also does not seem to use the `...` at all(?) But it seems that somehow between the `sapply` call and the `lapply` call the meaning of `...` changes. – shadow Aug 12 '14 at 08:20

1 Answers1

1

Because I am not a C master, at all, I can only show you what the differences are between how sapply and lapply are represented internally. And maybe someone else can build on this answer a little bit.

So for sapply: we'll look at https://github.com/SurajGupta/r-source/blob/master/src/library/base/R/sapply.R

And for lapply: https://github.com/SurajGupta/r-source/blob/master/src/library/base/R/lapply.R

This shows that although sapply calls lapply it then also calls simplify2array which could be one of the issues, but my intuition is telling me that this isn't the issue.

lapply ends up using .Internal(lapply(X,FUN)) which ends up calling do_lapply within the C source code here: https://github.com/SurajGupta/r-source/blob/91aaa43312da6b0e6906ef221fd7756dc0459843/src/main/apply.c

My guess is that because sapply inherently passes two parameters into SEXP args the ... argument is trying to create the SEXP args of sapply with everything after sapply(lst, "[", and because it can't figure out what to do with either 1, or j= before it gets to simplify=FALSE, USE.NAMES=FALSE, it fails. Although that would just be my guess. Whereas lapply doesn't have the additional arguments being forced into SEXP args so it can handle 1, or j= in a different way.

This post, Understanding how .Internal C functions are handled in R, does a very nice job explaining some of the nitty-gritty C stuff.

Hope this helps explain the differences to some degree, or at least can serve as a starting point for someone with greater R-devel skills.

Community
  • 1
  • 1
James Tobin
  • 3,070
  • 19
  • 35