-
Notifications
You must be signed in to change notification settings - Fork 0
/
poo.Rmd
196 lines (157 loc) · 6.63 KB
/
poo.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# Programação Orientada a Objetos
Como vimos anteriormente, o R é uma linguagem de programação orientada à
objetos. Dois conceitos fundamentais desse tipo de linguagem são os de
**classe** e **método**. Já vimos também que todo objeto no R possui uma
classe (que define sua estrutura) e analisamos algumas delas. O que
seria então um método? Para responder essa pergunta precisamos entender
inicialmente os tipos de orientação a objetos que o R possui.
O R possui 3 sitemas de orientação a objetos: **S3**, **S4**, e **RC**:
- **S3**: implementa um estilo de programação orientada a objeto chamada
de *generic-function*. Esse é o estilo mais básico de programação em R
(e também o mais utilizado). A ideia é que existam **funções
genéricas** que decidem qual método aplicar de acordo com a classe do
objeto. Os métodos são definidos da mesma forma que qualquer função,
mas chamados de maneira diferente. É um estilo de programação mais
"informal", mas possibilita uma grande liberdade para o programador.
- **S4**: é um estilo mais formal, no sentido que que as funções
genéricas devem possuir uma classe formal definida. Além disso, é
possível também fazer o **despacho múltiplo de métodos**, ao contrário
da classe S3.
- **RC**: (*Reference Classes*, antes chamado de R5) é o sistema mais
novo implementado no R. A principal diferença com os sistemas S3 e
S4 é que métodos pertencem à objetos, não à funções. Isso faz com que
objetos da classe RC se comportem mais como objetos da maioria das
linguagens de programação, como Python, Java, e C#.
Nesta sessão vamos abordar como funcionam os métodos como definidos pelo
sistema S3, por ser o mais utilizado na prática para se criar novas
funções no R. Para saber mais sobre os outros métodos, consulte o livro
[Advanced R](http://adv-r.had.co.nz/OO-essentials.html).
Vamos entender como uma função genérica pode ser criada através de um
exemplo. Usando a função `methods()`, podemos verificar quais métodos
estão disponíveis para uma determinada função, por exemplo, para a
função `mean()`:
```{r}
methods(mean)
```
O resultado são expressões do tipo `mean.<classe>`, onde `<classe>` é
uma classe de objeto como aquelas vistas anteriormente. Isso significa
que a função `mean()`, quando aplicada à um objeto da classe `Date`, por
exemplo, pode ter um comportamento diferente quando a mesma função for
aplicada à um objeto de outra classe (numérica).
Suponha que temos o seguinte vetor numérico:
```{r}
set.seed(1)
vec <- rnorm(100)
class(vec)
```
e queremos calcular sua média. Basta aplicar a função `mean()` nesse
objeto para obtermos o resultado esperado
```{r}
mean(vec)
```
Mas isso só é possível porque existe um método definido espcificamente
para um vetor da classe `numeric`, que nesse caso é a função
`mean.default`. A função genérica nesse caso é a `mean()`, e a função
método é a `mean.default`. Veja que não precisamos escrever o nome
inteiro da função genérica para que ela seja utilizada, como por exemplo,
```{r}
mean.default(vec)
```
Uma vez passado um objeto para uma função, é a classe do objeto que irá
definir qual método utilizar, de acordo com os métodos disponíveis. Veja
o que acontece se forçarmos o uso da função `mean.Date()` nesse vetor
```{r}
mean.Date(vec)
```
O resultado não faz sentido pois ele é específico para um objeto da
classe `Date`.
Pode-se ainda alterar a classe de um objeto, que passa a ser tratado
pelo método associado a esta classe, se houver.
```{r}
class(vec) <- "Date"
mean(vec)
```
Em S3 um objeto pode ter mais de uma classe e neste caso a procura por método segue a ordem de especificação.
Procura-se, sequencialmente, métodos para todos as classes definidas.
Veja esta sequência de três atribuições de classes.
```{r}
class(vec) <- c("numeric", "Date")
mean(vec)
class(vec) <- c("Date", "numeric")
mean(vec)
class(vec) <- c("foo", "Date")
mean(vec)
```
Em todos casos foi processada o método `mean.Date()`.
Na primeira e terceira porque não existe método específico para
classes `numeric` ou `foo`. Na segunda porque `Date` era a primeira classe
do objeto para qual há um método específico.
Tudo isso acontece por causa de um mecanismo chamado de **despacho de
métodos** (*method dispatch*), que é responsável por identificar a
classe do objeto e utilizar ("despachar") a função método correta para
aquela classe. Toda função genérica possui a mesma forma: uma chamada
para a função `UseMethod()`, que especifica o nome genérico e o objeto a
ser despachado. Por exemplo, veja o código fonte da função `mean()`
```{r}
mean
```
Agora veja o código fonte da função `mean.default`, que é o método
específico para vetores numéricos
```{r}
mean.default
```
Agora suponha que você ddeseja criar uma função que calcule a média para
um objeto de uma classe diferente daquelas previamente definidas. Por
exemplo, suponha que você quer que a função `mean()` retorne a média das
linhas de uma matriz.
```{r}
set.seed(1)
mat <- matrix(rnorm(50), nrow = 5)
mean(mat)
```
O resultado é a média de todos os elementos, e não de cada linha. Nesse
caso, podemos definir nossa própria função método para fazer o cálculo
que precisamos. Por exemplo:
```{r}
mean.matrix <- function(x, ...) rowMeans(x)
```
Uma função método é sempre definida dessa forma:
`<funçãogenérica>.<classe>`. Agora podemos ver novamente os métodos
disponíveis para a função `mean()`
```{r}
methods(mean)
```
e simplesmente aplicar a função genérica `mean()` à um objeto da classe
`matrix` para obter o resultado que desejamos
```{r}
class(mat)
mean(mat)
```
Esse exemplo ilustra como é simples criar funções método para
diferentes classes de objetos. Poderíamos fazer o mesmo para objetos das
classes `data.frame` e `list`
```{r}
mean.data.frame <- function(x, ...) sapply(x, mean, ...)
mean.list <- function(x, ...) lapply(x, mean)
```
Aplicando em objetos dessas classes específicas, obtemos:
```{r}
## Data frame
set.seed(1)
da <- data.frame(c1 = rnorm(10),
c2 = runif(10))
class(da)
mean(da)
## Lista
set.seed(1)
dl <- list(rnorm(10), runif(50))
class(dl)
mean(dl)
```
Obviamente esse processo todo é extremamente importante ao se criar
novas funções no R. Podemos tanto criar uma função genérica (como a
`mean()`) e diversos métodos para ela usando classes de objetos
existentes, quanto (inclusive) criar novas classes e funções método para
elas. Essa é uma das grandes lberdades que o método S3 de orientação à
objetos permite, e possivelmente um dos motivos pelos quais é
relativamente simples criar pacotes inteiros no R.