17

I'd like to check whether an R function's "..." (ellipsis) parameter has been fed with some values/arguments.

Currently I'm using something like:

test1 <- function(...) {
   if (missing(...)) TRUE
   else FALSE
}

test1()
## [1] TRUE
test1(something)
## [2] FALSE

It works, but ?missing doesn't indicate if that way is proper/valid.

If the above is not correct, what is THE way to do so? Or maybe there are other, faster ways? PS. I need this kind of verification for this issue.

gagolews
  • 12,836
  • 2
  • 50
  • 75
  • http://stackoverflow.com/questions/9877271/how-to-check-existence-of-an-input-argument-for-r-functions – GSee Nov 01 '14 at 00:47

3 Answers3

12

Here's an alternative that will throw an error if you try to pass in a non-existent object.

test2 <- function(...) if(length(list(...))) FALSE else TRUE

test2()
#[1] TRUE
test2(something)
#Error in test2(something) : object 'something' not found
test2(1)
#[1] FALSE
GSee
  • 48,880
  • 13
  • 125
  • 145
  • Sure, but I really wonder also if `missing(...)` is valid. :) BTW, for the sole purpose of testing if there's something under `...`, `length(list(...))==0` is a little bit [slower](https://github.com/Rexamine/stringi/issues/111). Anyway, thanks! – gagolews Nov 01 '14 at 10:19
  • @gagolews it should be slower because it's forcing evaluation. It's hard to say what's better without knowing why you're checking or what you're doing with that information. Maybe it's better to throw here, maybe it's better to throw later. Presumably, if you find out that there are arguments, you'll do something with them, which will `force` evaluation and the error. `length(list(...))` is also one more function call than `missing(...)` – GSee Nov 01 '14 at 13:27
  • 1
    BTW, you said `if (length(list(...))==0)`, and you used `if (length(list(...)) > 0)` in your benchmark. I think `if (length(list(...)))` is marginally faster (i.e. the implicity conversion to logical is faster than comparing to a numeric) – GSee Nov 01 '14 at 13:38
5

I think match.call is what you need:

test <- function(...) {match.call(expand.dots = FALSE)}

> test()
test()

> test(x=3,y=2,z=5)
test(... = list(x = 3, y = 2, z = 5))

It will give you every time the values passed in the ellipsis, or it will be blank if you won't pass any.

Hope that helps!

LyzandeR
  • 37,047
  • 12
  • 77
  • 87
  • If there were more parameters in the arg list (other than "..."), I should test for `any(names(match.call(expand.dots=FALSE)[-1]) == "...")`, don't I? :) – gagolews Nov 01 '14 at 10:08
  • 1
    If you necessarily want it to give you TRUE or FALSE then yeah the above works great. If you don't then the above function works with more parameters other than '...' too. As you already know `test <- function(k=7,...) {match.call(expand.dots = FALSE)}` for `test(x=3,y=2,z=5,k=5)` returns `test(k = 5, ... = list(x = 3, y = 2, z = 5))` – LyzandeR Nov 01 '14 at 10:24
0

If it helps anyone I ended up using the following function to get the ellipsis parameters for every function (returns an empty list or a list of arguments):

get.params <- function (...) {
  params <- list()

  if (length(list(...)) && !is.null(...)) 
    params <- unlist(...)

  return(params)
}

for:

f <- function(t, ...) {
 params <- get.params(...)
 print(paste(params))
}
Denis
  • 11,796
  • 16
  • 88
  • 150