How to use R's ellipsis feature when writing your own function?
ellipsis, function, parameters, r, variadic
Solution
I read answers and comments and I see that few things weren't mentioned:
`data.frame` uses `list(...)` version. Fragment of the code:
object <- as.list(substitute(list(...)))[-1L]
mrn <- is.null(row.names)
x <- list(...)
`object` is used to do some magic with column names, but `x` is used to create final `data.frame`. For use of unevaluated `...` argument look at `write.csv` code where `match.call` is used.
As you write in comment result in Dirk answer is not a list of lists. Is a list of length 4, which elements are `language` type. First object is a `symbol` - `list`, second is expression `1:10` and so on. That explain why `[-1L]` is needed: it removes expected `symbol` from provided arguments in `...` (cause it is always a list). As Dirk states `substitute` returns "parse tree the unevaluated expression". When you call `my_ellipsis_function(a=1:10,b=11:20,c=21:30)` then `...` "creates" a list of arguments: `list(a=1:10,b=11:20,c=21:30)` and `substitute` make it a list of four elements:
List of 4
$ : symbol list
$ a: language 1:10
$ b: language 11:20
$ c: language 21:30
First element doesn't have a name and this is `[[1]]` in Dirk answer. I achieve this results using:
my_ellipsis_function <- function(...) {
input_list <- as.list(substitute(list(...)))
str(input_list)
NULL
}
my_ellipsis_function(a=1:10,b=11:20,c=21:30)
As above we can use `str` to check what objects are in a function.
my_ellipsis_function <- function(...) {
input_list <- list(...)
output_list <- lapply(X=input_list, function(x) {str(x);summary(x)})
return(output_list)
}
my_ellipsis_function(a=1:10,b=11:20,c=21:30)
int [1:10] 1 2 3 4 5 6 7 8 9 10
int [1:10] 11 12 13 14 15 16 17 18 19 20
int [1:10] 21 22 23 24 25 26 27 28 29 30
$a
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.00 3.25 5.50 5.50 7.75 10.00
$b
Min. 1st Qu. Median Mean 3rd Qu. Max.
11.0 13.2 15.5 15.5 17.8 20.0
$c
Min. 1st Qu. Median Mean 3rd Qu. Max.
21.0 23.2 25.5 25.5 27.8 30.0
It's ok. Lets see `substitute` version:
my_ellipsis_function <- function(...) {
input_list <- as.list(substitute(list(...)))
output_list <- lapply(X=input_list, function(x) {str(x);summary(x)})
return(output_list)
}
my_ellipsis_function(a=1:10,b=11:20,c=21:30)
symbol list
language 1:10
language 11:20
language 21:30
[[1]]
Length Class Mode
1 name name
$a
Length Class Mode
3 call call
$b
Length Class Mode
3 call call
$c
Length Class Mode
3 call call
Isn't what we needed. You will need additional tricks to deal with these kind of objects (as in `write.csv`).
If you want use `...` then you should use it as in Shane answer, by `list(...)`.
Problem
The R language has a nifty feature for defining functions that can take a variable number of arguments. For example, the function `data.frame` takes any number of arguments, and each argument becomes the data for a column in the resulting data table. Example usage: ``` > data.frame(letters=c("a", "b", "c"), numbers=c(1,2,3), notes=c("do", "re", "mi")) letters numbers notes 1 a 1 do 2 b 2 re 3 c 3 mi ``` The function's signature includes an ellipsis, like this: ``` function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, stringsAsFactors = default.stringsAsFactors()) { [FUNCTION DEFINITION HERE] } ``` I would like to write a function that does something similar, taking multiple values and consolidating them into a single return value (as well as doing some other processing). In order to do this, I need to figure out how to "unpack" the `...` from the function's arguments within the function. I don't know how to do this. The relevant line in the function definition of `data.frame` is `object <- as.list(substitute(list(...)))[-1L]`, which I can't make any sense of. So how can I convert the ellipsis from the function's signature into, for example, a list? To be more specific, how can I write `get_list_from_ellipsis` in the code below? ``` my_ellipsis_function(...) { input_list <- get_list_from_ellipsis(...) output_list <- lapply(X=input_list, FUN=do_something_interesting) return(output_list) } my_ellipsis_function(a=1:10,b=11:20,c=21:30) ``` Edit It seems there are two possible ways to do this. They are `as.list(substitute(list(...)))[-1L]` and `list(...)`. However, these two do not do exactly the same thing. (For differences, see examples in the answers.) Can anyone tell me what the practical difference between them is, and which one I should use?