0

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?.

LAP
  • 6,605
  • 2
  • 15
  • 28
David
  • 371
  • 2
  • 13
  • 4
    It's very hard to understand what you have created so far and what you want to achieve, but I guess that ellipsis might help you: [How to use R's ellipsis feature when writing your own function?](https://stackoverflow.com/questions/3057341/how-to-use-rs-ellipsis-feature-when-writing-your-own-function) – pogibas Mar 28 '19 at 09:47
  • I want to create f.whatever <- function(x,y) return(x+y) without overriding the other functionality. Also, thank you for the link, it's useful although I don't see if it really gives me a good answer! – David Mar 28 '19 at 09:50
  • That's what S4 class functions do as far as I understand, but I never needed them. – moodymudskipper Mar 28 '19 at 12:14

5 Answers5

1

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
Julian_Hn
  • 2,086
  • 1
  • 8
  • 18
  • I think the final solution will be similar to this one, calling a different set of methods depending on whether missing(...) is TRUE or FALSE – David Mar 28 '19 at 11:26
  • 1
    This would work. Ellipsis are very nice in this case, cause you can easily add more arguments later. So if you want a function that can take 3 arguments, you would just reference them as `..1` and `..2` respectively. This can be expanded as much as you like. – Julian_Hn Mar 28 '19 at 11:36
1

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?!")
  }
}
Tom Haddow
  • 230
  • 1
  • 10
  • That would be a possibility, however the number of conditions I'd have to check is quite big and my code would look kind of messy – David Mar 28 '19 at 11:22
0

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"
TinglTanglBob
  • 627
  • 1
  • 4
  • 14
0

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.

Benjamin
  • 16,897
  • 6
  • 45
  • 65
  • Your function can be simplified by using UseMethod() in all the sections where the number of non-null arguments is constant – David Mar 28 '19 at 12:06
  • Doesn't `UseMethod` only allow you to point to a method for one object a time? I'm not sure how I'd use it to my advantage if I have an `x` of class `character`, but want the behavior to change based on the classes of the subsequent objects? Other than handling all that logic in the method, which seems equally rabbit-holeish. But I'm open to being taught. – Benjamin Mar 28 '19 at 12:18
0

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
moodymudskipper
  • 46,417
  • 11
  • 121
  • 167