1

I'm trying to understand how to turn functions into object oriented programming in R. So for example, how could the data and 2 functions below be turned into one object using S3 (and then S4)? (Perhaps some other simple data and functions would serve better as examples?)

data <- c(1, 2, 3)

# Function 1
adding_1 <- function(x){
  x <- x+1
}

# Function 2
subtracting_1 <- function(x){
  x <- x-1
}

And how would below functions be executed using the OOP.

data1 <- adding_1(data)
data1
data2 <- subtracting_1(data)
data2
Oscar Kjell
  • 1,599
  • 10
  • 32
  • What do you mean by "object oriented programming"? There are lots of different styles of that. S3 and S4 use a very different style from Java or C++. The R6 package is somewhat more similar to them. – user2554330 Feb 08 '20 at 15:47
  • I would like to see an example using S3 please. I think that this would help me understand OOP better. – Oscar Kjell Feb 08 '20 at 15:49
  • I would suggest going through the OO section of Hadley Wickham's "Advanced R". https://adv-r.hadley.nz/oo.html –  Feb 08 '20 at 15:58

1 Answers1

3

In R, object-oriented programming is implemented in several very different ways.
The S3 type of OO is the most used because it's very simple and yet does a good job at having what seems to be the same functions behave differently with different types of objects. A good reference is Advanced R by Hadley Wickham.

In R objects have attributes. One of these attributes is the special class attribute. You can see this with

x <- 1:3
y <- c(1, 2, 3)
class(x)    # "integer"
class(y)    # "numeric"

The S3 system is a function overloading system. A special function is defined, the generic. Then other functions, the methods, are defined to process objects depending or their classes. A method that must be defined is the default method.

Here I use your example to define first a generic and then the default method.

# Function 1
adding_1 <- function(x, ...) UseMethod("adding_1")
adding_1.default <- function(x, ...){
  x <- x + 1
  x
}

Now the methods for objects of class "list" and "data.frame".

adding_1.list <- function(x, ...){
  num <- sapply(x, is.numeric)
  x[num] <- lapply(x[num], adding_1)
  x
}
adding_1.data.frame <- function(x, ...){
  num <- sapply(x, is.numeric)
  x[num] <- lapply(x[num], adding_1)
  x
}

And the same for subtracting_1.

# Function 2
subtracting_1 <- function(x, ...) UseMethod("subtracting_1")
subtracting_1.default <- function(x){
  x <- x - 1
  x
}
subtracting_1.list <- function(x, ...){
  num <- sapply(x, is.numeric)
  x[num] <- lapply(x[num], subtracting_1)
  x
}
subtracting_1.data.frame <- function(x, ...){
  num <- sapply(x, is.numeric)
  x[num] <- lapply(x[num], subtracting_1)
  x
}

Test cases.

When called with x as an argument (or y above) it's the default method that is called since there is no adding_1.integer nor adding_1.numeric.

And the same goes for mat.

But when called with a data frame, a special processing is needed in order not to have the function try to add 1 to character strings or other types of non-numeric column vectors that might be in the data frame.

mat <- matrix(1:6, 3)
df1 <- data.frame(x = letters[1:5], y = rnorm(5), z = 101:105)

adding_1(x)
adding_1(mat)
adding_1(df1)

subtracting_1(x)
subtracting_1(mat)
subtracting_1(df1)
Rui Barradas
  • 70,273
  • 8
  • 34
  • 66
  • Please note that when I run subtracting_1(df1); I get Warning: In Ops.factor(left, right) : ‘-’ not meaningful for factors – Oscar Kjell Feb 08 '20 at 19:05
  • 1
    @Gorp Yes, you are right, there were 2 bugs in `subtracting_1`: 1) the string in `UseMethod` had a typo, the first `t` was missing; 2) what should be `subtracting_1.default` was missing the `default` part and therefore the generic was no longer a generic, another object of the same name had been created. – Rui Barradas Feb 08 '20 at 19:36