rnorouzian
rnorouzian

Reputation: 7517

Checking all function arguments length at once in R

As a sanity check, I want when any arguments in function seda below is a vector of length larger than 1, the function stops.

Question: Instead of individually listing all function arguments (here x, y, z), is there a way to use match.call or formals etc. such that ALL function arguments could be checked at once?

I tried the below with no success:

seda <- function(x, y, z){

  is.v <- function(...) lengths(list(...)) > 1
  if(is.v(match.call())) stop("Error") # instead of `is.v(x, y, z)`

  x + y + z
}

seda(2, 2, 3)
seda(c(2, 3), 2, 3)

Upvotes: 2

Views: 563

Answers (4)

rnorouzian
rnorouzian

Reputation: 7517

I highly appreciate the input of my expert colleagues. Using your valuable comments, I guess what I want is the following:

seda <- function(x, y, z){

if(lengths(list(get(names(match.call()[-1])))) > 1) stop("Error")

 x + y + z
}

seda(c(2, 3), 2, 3) 
seda(2, 2, 3)

Also we could perhaps use formals as well:

   seda <- function(x, y, z){

    if(lengths(list(get(names(formals(seda))))) > 1) stop("Error")
     x + y + z
     }

   seda(c(2, 3), 2, 3)
   seda(2, 2, 3)

Or formalArgs like this:

    seda <- function(x, y, z){

     if(lengths(list(get(formalArgs(seda)))) > 1) stop("Error")
       x + y + z
        }

       seda(c(2, 3), 2, 3)
       seda(2, 2, 3)

Upvotes: 0

G. Grothendieck
G. Grothendieck

Reputation: 269556

If they are tested all at once then the error message won't say which argument was the problem. The following tests them in a loop and does indicate which was the offending argument in the error message.

seda <- function(x, y, z) {
  argnames <- names(match.call()[-1])
  for(nm in argnames) if (length(get(nm)) != 1) stop(nm, " does not have length 1")
  x + y + z
}

# test - note that the error message names z
seda(10, 20, 1:2)
## Error in seda(10, 20, 1:2) : z does not have length 1

Of course if you really had 3 arguments it would be a lot simpler just to write it out. This also gives argument specific error messages:

seda <- function(x, y = 1, z) {
  stopifnot(length(x) == 1, length(y) == 1, length(z) == 1)      
  x + y + z
}

seda(10, 20, 1:2)
## Error: length(z) == 1 is not TRUE

Upvotes: 0

eipi10
eipi10

Reputation: 93811

match.call() will capture the arguments to the function, which can then be tested for length. We use sapply to return a vector with the length of each function argument, and the any function to test whether any of the arguments have a length greater than 1.

seda <- function(x, y, z){

  if(any(sapply(match.call()[-1], length) > 1)) stop("All arguments must be length 1")

  x + y + z
}

seda(2, 2, 3)
[1] 7
seda(c(2, 3), 2, 3)
Error in seda(c(2, 3), 2, 3) : All arguments must be length 1

Thanks to @erocoar for pointing out that match.call can be used instead of sys.call and that as.list is unnecessary.

Upvotes: 2

alistaire
alistaire

Reputation: 43334

You can tweak what you have a bit to get it to work:

seda <- function(...){
    stopifnot(lengths(list(...)) == 1)

    sum(...)
}

seda(1, 1, 1)
#> [1] 3

seda(1, 1, 1:2)
#> Error: lengths(list(...)) == 1 are not all TRUE

...or with named parameters,

seda_named <- function(x, y, z){
    stopifnot(lengths(list(x, y, z)) == 1)

    x + y + z
}

seda_named(1, 1, 1)
#> [1] 3

seda_named(1, 1, 1:2)
#> Error: lengths(list(x, y, z)) == 1 are not all TRUE

To use stop instead of stopifnot so as to control the error message, wrap the condition in all.

Upvotes: 1

Related Questions