4

I am confused by how list(...) works in R and S-plus. For following code

pp <- function(x, ...) { print( length( list(...)))}
pp(1,,,1)

In S-Plus, it works, but in R, it gives "Error in print(length(list(...))) : argument is missing, with no default"

I am more interest in how this works in R and how to get the value of list(...) in R functions.

Lii
  • 11,553
  • 8
  • 64
  • 88
Ossifragus
  • 43
  • 2
  • I was only going to suggest that you simply remove the extra commas in the version you have now: `pp(1,1)` and then `pp(1,2,3)` should return 1 and 2. – joran Jun 06 '13 at 21:47

2 Answers2

4

I'm not sure why that syntax is allowed in S-plus but not in R.

Here, though, is some R code that will have essentially the same effect:

pp <- function(x, ...) {
    print(length(as.list(match.call(expand.dots=FALSE))[["..."]]))
}
pp(1,,,1)
# [1] 3

Alternatively, using a trick from here:

ppp <- function(x, ...) {
    print(length(substitute(...())))
}
ppp(1,,,1)
# [1] 3
Community
  • 1
  • 1
Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
0

You cannot use unnamed (edit: ... and missing) arguments in the dots and any arguments after the dots when these arguments are intended to be matched in an arg-list. The positional matching that automatically occurs for unnamed arguments only "works" in the typical argument processing for named arguments(in the arg list) before the dots.

> pp1 <- function(x, ...) { length( list(...))}
> pp1(1,z=NULL,xx=NULL,1)
[1] 3
> pp2 <- function(x, z, ...) { length( list(...))}
> pp2(1,z=NULL,xx=NULL,1)
[1] 2
> pp3 <- function(x, z, ...) { length( list(...))}
> pp3(1, ,xx=NULL,1)
[1] 2

> pp <- function(x, ...) { length( list(...))}
> pp(1, , xx=NULL, 1)
Error in pp(1, , xx = NULL, 1) : argument is missing, with no default

Reading the help page for match.call, the second "commonly used circumstance" is described as:

To pass most of the call to another function, often model.frame. Here the common idiom is that expand.dots = FALSE is used, and the ... element of the matched call is removed.

The sequence of argument matching (when not circumvented) is described in section 4.3.2 "Argument matching":

  1. Positional matching. Any unmatched formal arguments are bound to unnamed supplied arguments, in order. If there is a ‘...’ argument, it will take up the remaining arguments, tagged or not.

If any arguments remain unmatched an error is declared.

IRTFM
  • 258,963
  • 21
  • 364
  • 487
  • 1
    I'm not sure what you mean here by "you cannot use unnamed arguments in the dots" - clearly you can, as Josh demonstrated above...? – eddi Jun 06 '13 at 22:41
  • His function is not really using the standard argument list mechanism. It is treating the function call as an unevaluated expression. – IRTFM Jun 06 '13 at 22:49
  • I'm probably missing smth. Ok, it's not standard, but so what? Afaict he could later process that list any way he likes, including but not limited to all of the standard processing – eddi Jun 06 '13 at 22:53
  • @eddi: As originally stated I agree I was wrong. I'm amending my answer, but probably not to your liking, since I think the match.call(expand.dot=FALSE) is not really "using" the dots-mechanism, but is "going around behind its back". – IRTFM Jun 06 '13 at 22:57
  • ok... you might want to check out `alist` as an example of a function that was intended to do exactly what you think you're not supposed to do - although it seems like that function would fare better if it used `match.call` as it currently fails inside OP's function, but wouldn't if it had Josh's implementation instead of `sys.call`. And then, incidentally, OP's problem could be solved by simply calling `alist` instead of `list`. – eddi Jun 06 '13 at 23:05
  • I didn't say you are not supposed to use `match.call` (or `alist`). I'm just saying they are not "using" the typical argument list evaluation of "...". – IRTFM Jun 06 '13 at 23:09
  • @eddi & @DWin. It does seem to me like this is mostly a matter of `list(...)` not having been supplied with consistent (or expected?) behavior for this corner case. In any case, check this out: `j <- function(x,...) {paste0(..3, ..4)}; j(1,2,3,4,5,6)`. Interesting, no? – Josh O'Brien Jun 06 '13 at 23:50
  • Seemed to behave as expected. The third and fourth item of "..." got `paste0()`-ed. You didn't have any missing arguments. What I found even more interesting was that this does not throw an error: `j(1, ,3,4,5,6)` and even more surprising to me, this does not either: `j(1, ,3, ,5,6)` – IRTFM Jun 06 '13 at 23:54
  • DWin -- Oops. That's what I meant to type. Thanks for figuring that out. – Josh O'Brien Jun 07 '13 at 00:05