Uma das principais atividades e, também uma boa prática na programação usando o R é a utilização da abordagem de programação orientada a objetos. Essa proposta é útil na construção de pacotes no R bem como na organização e estabilidade das bibliotecas criadas. Nesse tutorial iremos trabalhar com a classe S4 de objetos do R.
Suponha que desejamos criar um pacote que nos ajude a calcular a menção de alunos de uma turma. Nesse caso, o sistema de classes S4 pode ser definido criando-se o objeto aluno:
1 2 3 4 5 | setClass ( "Aluno" , representation (nome= "character" , idade= "numeric" ), prototype (nome= character (0), idade= numeric (0)) ) |
Esse objeto contêm, inicialmente, dois slots: a idade do aluno e seu nome. O nome é considerado nesse modelo uma variável caracter e a idade uma variável numérica. O comando prototype define quais devem ser os valores iniciais para esses dois atributos.
Entretanto, na universidade nós temos diversos tipos de aluno, eles podem ser: alunos da graduação, pós-graduação, extensão, aluno especial, ouvinte, etc. Suponha que tenhamos dois tipos de alunos apenas:
1 2 3 4 5 6 7 8 | #Define as subclasses de alunos setClass ( "Graduacao" , representation (nota1= "numeric" ,nota2= "numeric" ,nota3= "numeric" ), contains= "Aluno" ) setClass ( "Posgraduacao" , representation (artigo= "character" ,nota1= "numeric" ,nota2= "numeric" ), contains= "Aluno" ) |
Na primeira subclasse donominada Graduação o aluno faz três provas representadas pelos slots: nota1, nota2 e nota3. Já na segunda subclasse o aluno é um aluno de Pós-graduação e ele é avaliado de maneira diferente, tem que preparar um artigo o qual pode ser armazenado no slot artigo e faz duas provas cujas notas são armazenadas em nota1 e nota 2. Note que nesse caso, ambos são Alunos e por isso herdam os atributos de nome e idade.
Além do mais, na criação dessas subclasses não atribuímos os valores iniciais desses atributos por opção. A boa prática, no entanto, recomenda que todos esses atributos sejam definidos os valores de inicialização através do método prototype.
Cada tipo de aluno terá sua nota calculada de maneira diferente: para os alunos de graduação a nota final será a média das três notas nas provas, já os alunos de Pós-graduação a nota será 50% da média das duas provas e 50% se ele fez o artigo e zero se não fez. Nesse caso, usaremos a propriedade de Mutabilidade (Mutability) dos sistemas de programação orientada a objetos. Através dessa propriedade um cômputo diferente será realizado para cada tipo de objeto. O primeiro passo é a criação de um método genérico:
1 2 3 4 5 6 7 8 | #Cria um método generico denominado calculaMencao #o qual é aplicado para o tipo de objeto de entrada. setGeneric ( "calculaMencao" , function (object) { standardGeneric ( "calculaMencao" ) } ) |
Em seguida, vamos definir um cálculo de função para cada tipo de aluno:
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 | #Para alunos de graduação setMethod ( "calculaMencao" , signature ( "Graduacao" ), function (object) { #Calcula a média media<-(object@nota1+object@nota2+object@nota3)/3 #Retorna o resultado return (media) } ) #Para alunos de pós-graduação setMethod ( "calculaMencao" , signature ( "Posgraduacao" ), function (object) { #Calcula a média media<-(object@nota1+object@nota2)/2 #Fez o artigo ? artigo<- ifelse (object@artigo == "" ,0,10) #Calcula a nota final nota<-media*0.5+artigo*0.5 #Retorna o resultado return (nota) } ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #Aluno de Graduação joao <- new ( "Graduacao" , nome= "João Silva" , idade=22, nota1=2.75, nota2=8.78, nota3=5.72) #Aluno de Pós-Graduação maria <- new ( "Posgraduacao" , nome= "Maria Silva" , idade=27, nota1=8.54, nota2=3.21, artigo= "Como programar em R" ) #Aluno de Pós-Graduação alex <- new ( "Posgraduacao" , nome= "Alexandre Silva" , idade=33, nota1=2.15, nota2=5.48, artigo= "" ) |
1 2 3 4 5 6 7 8 9 | #Cria o constructor: Graduacao <- function (nome, idade, nota1, nota2, nota3){ new ( "Graduacao" , nome=nome, idade=idade, nota1=nota1, nota2=nota2, nota3=nota3) } #Cria o constructor: Posgraduacao <- function (nome, idade, nota1, nota2, artigo){ new ( "Graduacao" , nome=nome, idade=idade, nota1=nota1, nota2=nota2, artigo=artigo) } |
1 2 3 4 5 6 7 8 9 10 11 | #Calcula a nota do João resultadoJoao<- calculaMencao (joao) resultadoJoao #Calcula a nota da Maria resultadoMaria<- calculaMencao (maria) resultadoMaria #Calcula a nota do Alex resultadoAlex<- calculaMencao (alex) resultadoAlex |
1 2 | setClass ( "Maluco" , contains= c ( "Graduacao" , "Posgraduacao" )) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #Registro do Sergio na graduação sergioGraduacao <- new ( "Maluco" , nome= "Sergio Silva" , idade=22, nota1=8.36, nota2=9.12, nota3=5.64) #Registro do Sergio na pós-graduação sergioPosgraduacao <- new ( "Posgraduacao" , nome= "Sergio Silva" , idade=22, nota1=3.45, nota2=8.34, artigo= "Não sei nada" ) #Calcula da nota do Sergio resultadoSergio1<- calculaMencao (sergioGraduacao) resultadoSergio1 resultadoSergio2<- calculaMencao (sergioPosgraduacao) resultadoSergio2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | setClass ( "Graduacao" , representation ( nota1= "numeric" , nota2= "numeric" , nota3= "numeric" ), validity = function (object) { if (object@nota1<0 | object@nota1>10 ) { stop ( "A nota na prova 1 tem que ser válida." ) } if (object@nota2<0 | object@nota2>10 ) { stop ( "A nota na prova 2 tem que ser válida." ) } if (object@nota3<0 | object@nota3>10) { stop ( "A nota na prova 3 tem que ser válida." ) } return ( TRUE ) }, contains= "Aluno" ) |
- slotNames: fornece o nome dos slots.
- getSlots: fornece o nome dos slots e seus tipos.
- getClass: fornece o nome dos slots, seus tipos e das classes antecessoras.