David
David

Reputation: 365

Can an R function behaiviour change depending of number of arguments received?

So far I have created a function that can change its behaiviour depending on whether it receives a number or a character as input, a minumal example could be the following:

...

f <- function(x)
  UseMethod("g")

f.numeric <- function(x)
  return(x^2)

f.character <- function(x)
  return("Hey, dude. WTF are you doing? Don't give me characters!")

...

Now assume that I want f to be able to receive two numbers as input and return its sum, without losing the previous functionality. How can I achieve that?.

Upvotes: 0

Views: 71

Answers (5)

moodymudskipper
moodymudskipper

Reputation: 47300

If you want to keep using S3 you could use ...length() (>= R 3.4.2) :

f <- function(...)
  UseMethod("f")

f.numeric <- function(...)
  if(...length() == 1) ..1^2 else sum(...) 

f.character <- function(...)
  return("Hey, dude. WTF are you doing? Don't give me characters!")

f(2)
#[1] 4

f(3,4)
# [1] 7

Upvotes: 0

Benjamin
Benjamin

Reputation: 17369

If what you're looking for is something like C's method signatures[1], then no, I'm not aware that R has anything of that nature.

The closest I'm aware of in R is that you have a "super-function" that accepts all of the arguments and then a set of sub-functions to which the super-function distributes. For example, consider (what I've outlined below isn't functionally different than Julian_Hn's answer. The difference between using ellipses and explicitly naming the arguments is the amount of control over what they user can pass to the function. If you use ellipses, your test for the existence of the argument will look different)

super_function <- function(x = NULL, y = NULL){
  if (!is.null(x) & is.null(y)){
    if (is.character(x)){
      sub_function_xchar(x)
    } else if {
      (is.numeric(x)){
        sub_function_xnum(x)
      }
    } else {
      sub_function_xelse(x)
    }
  } else {
    if (!is.null(x) & !is.null(y)){
      if (is.character(x) & is.character(y)){
        sub_function_xychar(x, y)
      } else {
        # Okay, I think you might get the point now
      }
    }
  }
}

sub_function_xchar <- function(x){
  # whatever you want to do with x as a character
}

sub_function_xnum <- function(x){
  # whatever you want to do with x as a numeric
}

sub_function_xelse <- function(x){
  # whatever you want to do with any other class of x
}

sub_function_xychar <- function(x, y){
  # whatever you want to do with x and y as characters
}

Yes, it's messy. I've used approaches like this with success for small sets of arguments. I don't know that I'd recommend it for large sets of arguments. Instead, if you have a lot of arguments, I'd recommend finding ways to break whatever task you're intending into smaller chunks that can each be isolated to their own functions.

[1] Not sure if I got the term right, but the functionality in C that many methods may have the same name, but they must be unique on the collection and type of arguments they accept.

Upvotes: 0

TinglTanglBob
TinglTanglBob

Reputation: 647

Not sure if this is what you need, but maybe it helps :)

sum_them <- function(var1, var2, na.rm = F)
{
  if(all(is.numeric(c(var1, var2)))) return(sum(c(var1, var2), na.rm = na.rm))
  return("non numeric argument")

}

sum_them("test", "this")
sum_them("test", 10)
sum_them(5, "this")
sum_them(5, 10)
sum_them(NA, 10)
sum_them(NA, 10, na.rm = T)

Output

> sum_them("test", "this")
[1] "non numeric argument"
> sum_them("test", 10)
[1] "non numeric argument"
> sum_them(5, "this")
[1] "non numeric argument"
> sum_them(5, 10)
[1] 15
> sum_them(NA, 10)
[1] NA
> sum_them(NA, 10, na.rm = T)
[1] 10

Updated function, since i didn't get the do something different if it is just 1 number.

Logic behind: if there is just 1 paramter (var1) do whatever you like whit it, but trycatch in case it is a no nummeric. If all param are numeric, sum them up. else return some string.

   sum_them <- function(var1, ..., na.rm = F)
{
  if(missing(...)) tryCatch({var1 <- var1^2}, warning = function(w){}, error = function(e){})
  if(all(is.numeric(c(var1, ...)))) return(sum(c(var1, ...), na.rm = na.rm))
  return("non numeric argument")

}

new output:

> sum_them("test", "this")
[1] "non numeric argument"
> sum_them("test", 10)
[1] "non numeric argument"
> sum_them(5, "this")
[1] "non numeric argument"
> sum_them(5, 10)
[1] 15
> sum_them(NA, 10)
[1] NA
> sum_them(NA, 10, na.rm = T)
[1] 10
> sum_them(NA, na.rm = T)
[1] 0
> sum_them(10, na.rm = T)
[1] 100
> sum_them(10)
[1] 100
> sum_them("test")
[1] "non numeric argument"
> sum_them(10,10,10,10, NA)
[1] NA
> sum_them(10,10,10,10, NA, na.rm = T)
[1] 40
> sum_them(10,10,10,test, NA, na.rm = T)
[1] "non numeric argument"

Upvotes: 0

Julian_Hn
Julian_Hn

Reputation: 2141

With ellipsis this is easily possible:

f <- function(x,...)
{
  if(missing(...))
  {
    if(is.numeric(x)) return(x^2)
    if(is.character(x)) return("Hey, dude. WTF are you doing? Don't give me characters!")
  }else
  {
    if(any(is.character(c(x,...)))  return("Hey, dude. WTF are you doing? Don't give me characters!"))
    return(x+..1)
  }
}

> f("foo")
[1] "Hey, dude. WTF are you doing? Don't give me characters!"
> f(4)
[1] 16
> f(4,5)
[1] 9

Upvotes: 1

Tom Haddow
Tom Haddow

Reputation: 230

Could rewrite the function to do the checks yourself? e.g...

f <- function(x, y=NA){
  if (all(is.numeric(c(x,y))) & !is.na(y)){
    return(x+y)
  }else if(is.numeric(x)){
    return(x^2)
  }else if(is.character(x)){
    return("Hey, dude. WTF are you doing? Don't give me characters!")
  }else{
    return("Hey, dude. I don't know what you are giving me?!")
  }
}

Upvotes: 1

Related Questions