51

I have a function defined as

myFun <- function(x, y, ...) {
  # using exists
  if (exists("z")) { print("exists z!") }
  # using missing
  try(if (!missing("z")) { print("z is not missing!") }, silent = TRUE)
  # using get
  try(if (get("z")) { print("get z!") }, silent = TRUE)

  # anotherFun(...)
}

In this function, I want to check whether user input "z" in the argument list. How can I do that? I tried exists("z"), missing("z"), and get("z") and none of them works.

danioyuan
  • 1,268
  • 2
  • 13
  • 16
  • Can you show exactly how you've used `missing`? Because AFAIK that's the correct function to use. – joran Mar 26 '12 at 18:06
  • It would help if you provide a bit more context. There may be a better way to do what you're trying to accomplish. – Joshua Ulrich Mar 26 '12 at 18:15
  • 1
    @joran, `missing()` only applies for argument. Here there is no argument `z`, it can only be entered as part of `...` – Sacha Epskamp Mar 26 '12 at 18:18
  • @SachaEpskamp I agree. I simply wasn't sure if what the OP wrote was actually what they were doing. – joran Mar 26 '12 at 18:20
  • I've modified the code in the question to make it easier to understand. Thanks for the comments. – danioyuan Mar 26 '12 at 18:42

4 Answers4

82

I think you're simply looking for hasArg

myFun <- function(x, y, ...) { 
  hasArg(z)
}

> myFun(x=3, z=NULL)
[1] TRUE

From ?hasArg:

The expression hasArg(x), for example, is similar to !missing(x), with two exceptions. First, hasArg will look for an argument named x in the call if x is not a formal argument to the calling function, but ... is. Second, hasArg never generates an error if given a name as an argument, whereas missing(x) generates an error if x is not a formal argument.

GSee
  • 48,880
  • 13
  • 125
  • 145
33

@Sacha Epskamp has a pretty good solution, but it doesn't always work. The case where it fails is if the "z" argument is passed in as NULL...

# Sacha's solution
myFun <- function(x, y, ...) { 
  args <- list(...)
  exist <- !is.null(args[['z']])
  return(exist)
}

myFun(x=3, z=NULL) # FALSE, but should be TRUE!


# My variant
myFun2 <- function(x, y, ...) {
  args <- list(...)
  exist <- "z" %in% names(args)
  exist
}

myFun2(x=3, z=NULL) # TRUE
Tommy
  • 39,997
  • 12
  • 90
  • 85
10

There might be instances when you might not want to call list(...), since this will evaluate all the expressions in the dots. For example,

myFun <- function(x, y, ...){
  myArgs <- list(...)
  zInArgs <- ("z" %in% names(myArgs))
  return(zInArgs)
}

myFun(x = 2, y = "Happy", z = list(rep(rnorm(2e6), 100)))

This will take a long time. Instead, use match.call():

myFun <- function(x, y, ...){
  myArgs <- match.call()
  zInArgs <- ("z" %in% names(myArgs))
  return(zInArgs)
}

myFun(x = 2, y = "Happy", z = list(rep(rnorm(2e6), 100)))

The first example is still chugging away on my machine, while the second example should take practically no time at all.

EDIT:

To answer the comment from @CarlWitthoft:

R> system.time(
+   (myAns <- myFun(x = 2, y = "Happy", z = list(rep(rnorm(2e6), 100))))
+ )
   user  system elapsed 
      0       0       0 
R> myAns
[1] TRUE
BenBarnes
  • 19,114
  • 6
  • 56
  • 74
5

Here is a way I often do it. First convert ... to a list, then check if the elements are not NULL:

myFun <- function(x, y, ...) { 
args <- list(...)
exist <- !is.null(args[['z']])
return(exist)
}

Some results:

> myFun()
[1] FALSE
> myFun(z=1)
[1] TRUE
Sacha Epskamp
  • 46,463
  • 20
  • 113
  • 131
  • I don't think this is the intended way `...` is supposed to be used though. It is more a way of passing arguments to another function, but this is how I currently use it in my package as well (I really should change that:)) – Sacha Epskamp Mar 26 '12 at 18:21
  • 1
    Sacha, I do agree with you. If "z" is used in this function, it should not be in the ... But very often code is already written and making one change in the argument list can introduce errors that requires many changes to fix. So this is my lazy way to make it work. :) – danioyuan Mar 26 '12 at 18:34
  • 1
    I added an alternative solution that also handles `myFun(z=NULL)` – Tommy Mar 26 '12 at 18:35
  • @danioyuan Also this gives you awesome hidden and undocumented arguments that only you know about! – Sacha Epskamp Mar 26 '12 at 18:42
  • @danioyuan: the safe, and possibly "proper," way to add an argument is to place it last in the list with a default value assigned, i.e. `myfunc(x,y,...)` becomes `myfunc(x,y,z=1,...)` . AFAIK that will provide full back-compatibility. – Carl Witthoft Mar 26 '12 at 19:38