Builder and inheritance in R

8

It's a theoretical question, but it might help to understand the programming logic in r .

  • What is a constructor in the r language? What is its use?

  • What is and how inheritance works in r ? How to identify the inheritance of an object and what is the usefulness of knowing its inheritance?

asked by anonymous 08.11.2018 / 14:56

2 answers

9

First, I think this question has gone unanswered so far because these concepts are little (not to say "nothing") important to becoming a good R developer (unlike other languages).

In my answer I will explain a bit about the main structures and data types in R and I believe this will help you better understand the language.

In R, the basic data structure is vector (I'm using the English name so we do not confuse). The vector s can be:

  • atomic - are the normal vectors we know, with only one type .
  • lists - are lists of R, which can be heterogeneous.

The vectors have 3 important properties:

  • They have a method length - that is, we can always call length(vetor) .
  • They have a type, identified by the typeof function. the types can be inteiro , double , character or list (in addition to special types for functions such as closure or builtin ).
  • Can have attributes - use the attributes function to get them.

The vast majority of objects in R are created from combinations of vector s and their attributes.

Examples

An array in R is nothing more than an atomic vector with the attribute dim completed:

> x <- matrix(1:10, ncol = 2)
> y <- 1:10
> attr(y, "dim") <- c(5, 2)
> 
> identical(x, y)
[1] TRUE
A data.frame is a list where all elements must have the same length , the class attribute is data.frame and has a row.names attribute:

> x <- data.frame(a = 1:5, b = 1:5)
> y <- list(a = 1:5, b = 1:5)
> attr(y, "class") <- "data.frame"
> attr(y, "row.names") <- 1:5
> 
> identical(x, y)
[1] TRUE

Even the result object of a lm in R is a list, with other attributes filled in. For example, the class is lm :

> mod <- lm(mpg ~cyl, data = mtcars)
> typeof(mod)
[1] "list"
> attributes(mod)
$names
 [1] "coefficients"  "residuals"     "effects"       "rank"          "fitted.values" "assign"        "qr"           
 [8] "df.residual"   "xlevels"       "call"          "terms"         "model"        

$class
[1] "lm"

Conclusion

Nearly all objects in R are combinations of vector s + their attributes.

Object orientation in R

In R there are at least 4 ways to create object-oriented code. To better understand the inheritance between classes and etc., it is worth reading that chapter from the book Advanced R .

    
27.11.2018 / 17:20
6

The has at least 4 object-oriented systems (OO): S3 , S4 , Reference Classes (also known as R5 or RC ) and R6 . The latest system is not part of , but has been widely used by the community. R6 comes from a package with the same name.

  

But attention !!! You should even use OO in R?

It is not a matter of coming to defend this or that programming paradigm. It occurs that "R is a functional programming language; embrace it, do not fight it" 1 .

That is, if you really want to "understand the logic of programming in R ", you should start to address the problems in a more functional way. When your problem requires , leave the and use object orientation.

See, S3 system is an object-oriented system subordinated to functional programming logic, since its purpose is to dispatch the object correctly to the appropriate function (method).

Back to answer

Already exists a question about the difference between the native systems of the . I'll focus on the constructor and inheritance response on S3 and R6 systems since they are the most used.

constructors

S3

In% w / o there is no formal definition of the class. Any object can be "transformed" into the desired class.

x <- "lala"
class(x) <- "lm"
print(x)
# Error: $ operator is invalid for atomic vectors

To create or instantiate a class simply add the class to the object, as in the example above, or add it by means of the function S3 . Good practice, however, is to create a constructor to create objects of that class.

pergunta <- function(x) {
  structure(x, class = c("pergunta", class(x)))
}

p1 <- pergunta("Acabei de criar uma classe?")
p1
# [1] "Acabei de criar uma classe?"
# attr(,"class")
# [1] "pergunta"  "character"

R6

In structure() you need to first formally define the class. This is where you define a constructor (item R6 in initialize ).

library(R6)
Pergunta<- R6Class(
  "Pergunta",
  public = list(
    initialize = function(pergunta) {
      self$pergunta <- pergunta
      self$responder()
    },
    pergunta = NULL,
    responder = function() {
      print(sample(c("Sim!", "Não!"), 1))
    }
  )
)

Then you can instantiate it (which is when the constructor is "enabled")

set.seed(1)
P1 <- Pergunta$new("Acabei de criar uma classe?")
# [1] "Sim!"

On constructor utility in public : is the same as in any other language in which OO is used, but this paradigm has restricted use in R , as commented by Daniel.

inheritance

S3

Inheritance in the R method works by adding classes ahead of the "parent class". This is because the way to dispatch the object to the methods goes through the class vector until it finds a method for that function.

class(dplyr::starwars)
# [1] "tbl_df"     "tbl"        "data.frame"

Thus, S3 s can work for both methods ( tibble ), which are preferred, as well as methods of print() ( data.frame ). This is also why, even though we have not defined a method to print our class, summary() has turned around. When no method is found, the object is thrown to the "default method" ( R )

R6

In the case of nome_da_funcao.default() , the function that defines the class has an argument to identify inheritance ( R6 ). Attention to the fact that it should be passed its own class as object, not just its name.

PerguntaRetorica <- R6Class(
  "PerguntaRetorica", 
  inherit = Pergunta,
  public = list(responder = function() print("!?!"))
)

After the class has been defined, we can instantiate it.

retorica <- PerguntaRetorica$new("Ser ou não ser?")
[1] "!?!"

The importance of knowing the ascendancy of a given object is knowing how it will behave with the methods. And the way to do this is to pass the object to inherit .

class(retorica)
[1] "PerguntaRetorica" "Pergunta" "R6" 

Here we see that our daughter-class is heiress of class() which in turn descends from Pergunta

References

1: The tidy tools manifesto

2: Advanced R - Object oriented programming

    
05.12.2018 / 20:40