Reputation: 7517
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
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
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
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
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