Parte I: Conceitos Básicos de Programação no R

Funções e o Conceito de Variável Local

Nesse capítulo veremos como criar funções dentro do R para tornar nosso código mais dinâmico e prático. Além disso será apresentado o conceito de variável local, que é de grande importância em qualquer linguagem de programação.

Funções

Uma função dentro dentro de uma linguagem de programação é definida pelos seguintes itens:

  • nome da função;
  • argumentos (entrada);
  • sequência de comandos (corpo);
  • retorno (saída).

Tanto os argumentos de entrada quanto a saída de uma função podem ser nulos, ou vazios, mas em geral eles são definidos.

Depois que uma função é definida para executar a sua sequência de comandos basta chamá-la pelo nome, passando os argumentos de entrada caso estes não sejam nulos. Veremos alguns exemplos ao longo do capítulo.

Para definir uma nova função no R deve ser usada a seguinte sintaxe.

nome_da_funcao <- function(argumentos){ 
  # sequencia de comandos 
  return(saida) 
}
Nome Descrição
Argumentos: Define as variáveis cujos valores serão atribuídos pelo usuário quando a função for chamada.
Corpo da Função: Contém os cálculos e tarefas executadas pela função.
Retorno: Indica qual o valor que a função retorna como saída.

Vejamos alguns exemplos. Primeiro vamos construir uma função que retorna o maior entre dois valores passados como argumentos. Essa função já existe pronta no R e se chama , o que vamos fazer é entender como ela funciona criando a nossa própria função.

maior <- function(a,b){
   if(a>b)
       return(a)
   else
       return(b)
} 

Depois da função definida e compilada podemos chamá-la sem ter que digitar todo o código novamente. Veja o que acontece quando a função é chamada no prompt do R.

maior(3,2)
## [1] 3
maior(-1,4)
## [1] 4
maior(10,10)
## [1] 10
maior("A","B")
## [1] "B"

Também podemos optar por guardar a saída da função em uma variável, o que pode ser bastante útil.

x = maior(-5,-3)
y = 2*x
x
## [1] -3
y
## [1] -6

E se quiséssemos encontrar o maior entre 3 números? Temos duas alternativas. A primeira é usar a função acima composta com ela mesma. Veja como.

maior(1,maior(2,5))
## [1] 5
maior(10,maior(6,-1))
## [1] 10
maior(-3,maior(0,1))
## [1] 1

Outra alternativa é criar uma nova função com três argumentos própria para isso.

maior_de_3 <- function(a,b,c){
   if(a>b && a>c){
       return(a)
   } else {
       if(b>c)
           return(b)
       else
           return(c)
   }
}
y <- maior_de_3(2,15,6)
y
## [1] 15

Vamos agora fazer uma função que recebe como argumento um número natural n e retorna um array com os n primeiros múltiplos de 3. Nas atividades práticas do último capítulo fizemos isso para n=100, agora a ideia é criar uma função que realizará essa tarefa para qualquer n escolhido pelo usuário.

multiplos_3 <- function(n){
  vet <- NULL
  for(i in 1:n){
    vet[i] <- 3*i
  }
  return(vet)
}
multiplos_3(10)
##  [1]  3  6  9 12 15 18 21 24 27 30
multiplos_3(15)
##  [1]  3  6  9 12 15 18 21 24 27 30 33 36 39 42 45

E se colocarmos como argumento um número que não seja natural, o que acontece? A função executa os comandos considerando o n que foi passado como argumento.

multiplos_3(1.5)
## [1] 3
multiplos_3(-1)
## [1] 3

Caso o programador não queira que um usuário use essa função para números não naturais ele precisa dentro da função checar se o valor de n passado pelo usuário está correto. Caso o valor não seja correto é possível usar a função stop para imprimir uma mensagem de erro e interromper a execussão da função.

Veja um exemplo para a função multiplos_3, onde o argmento n será restrito para os naturais.

multiplos_3 <- function(n){
  if((n<=0)||(n%%1 != 0)){
    stop("n tem que ser um numero natural")
  }
  vet <- NULL
  for(i in 1:n){
    vet[i] <- 3*i
  }
  return(vet)
}

Variáveis Locais

As variáveis locais são aquelas usadas somente dentro do corpo da função. Para garantirmos que essa variável realmente não está assumindo um valor pré definido é preciso que ela seja iniciada dentro do corpo da função.

Reveja o código da função multiplos_3. Veja que a variável vet é um variável que só faz sentido dentro da função, está é uma variável local. Ao digitar vet <- NULL garantimos que essa variável dentro da função vai sempre começar como nula. Se essa linha de comando for omitida, a função pode retornar resultados não esperados pelo programador. Vejaomos alguns exemplos. Primeiro a forma correta.

vet = c(1:10)
m   = multiplos_3(5)
m
## [1]  3  6  9 12 15
vet
##  [1]  1  2  3  4  5  6  7  8  9 10

Veja no código acima que tanto m quanto vet gurdam os valores esperados tanto pelo programador quanto pelo usuário. O usuário da função multiplos_3 não tem como saber que dentro dessa função existe uma variável local chamada vet, então ele pode usar esse mesmo nome para outra variável, e isso não deve ser um problema.

Veja agora o que acontece se na função a variável local vet não for iniciada. Para isso a função multiplos_3 será implementada de maneira errada.

multiplos_3_errada <- function(n){
  if((n<=0)||(n%%1 != 0)){
    stop("n tem que ser um numero natural")
  }
  for(i in 1:n){
    vet[i] <- 3*i
  }
  return(vet)
}

Fazendo o mesmo teste que antes, agora com a função errada, veja o que acontece com variável m, que é a saída da função.

vet = c(1:10)
m   = multiplos_3_errada(5)
m
##  [1]  3  6  9 12 15  6  7  8  9 10
vet
##  [1]  1  2  3  4  5  6  7  8  9 10

Já a variável vet criada pelo usuário permanece a mesma mesmo após a função multiplos_3_errada ser chamada.

Dentro de uma função podemos ter dois tipos de variáveis:

  • os argumento de entrada
  • as variáveis locais

Os argumentos de entrada não podem ser iniciados dentro do corpo da função, nesse caso perderíam o valor informado pelo usuário. Já as variáveis locais, aquelas que não foram passadas como argumento, tem que ser iniciadas dentro da função.

Veja mais um exemplo de função antes da seção de exercícios. Suponha que queremos criar uma função que em vez de retornar os n primeiros múltiplos de 3 passe a retornar os n primeiros múltiplos de m.

multiplos <- function(n,m){
   if((n<=0)||(n%%1 != 0)){
       stop("n tem que ser um natural")
   }
   if((m<=0)||(m%%1 != 0)){
       stop("m tem que ser um natural")
   }     
   vet <- NULL
   for(i in 1:n){
       vet[i] <- m*i
   }
   return(vet)
}
multiplos(10,7)
##  [1]  7 14 21 28 35 42 49 56 63 70

Mas essa não é a única maneira de implementar tal função. Podemos fazer isso de várias forma diferete, por exemplo, usando os controles de fluxo while ou repeat.

multiplos_while <- function(n,m){
  if((n<=0)||(n%%1 != 0)){
    stop("n tem que ser um natural")
  }
  if((m<=0)||(m%%1 != 0)){
    stop("m tem que ser um natural")
  }     
  vet = NULL
  i = 0   #i representa quantos elementos ja foram alocados em vet
  while(i<n){
    i = i + 1
    vet = c(vet,m*i)
  }
  return(vet)
}
multiplos_while(10,7)
##  [1]  7 14 21 28 35 42 49 56 63 70
multiplos_repeat <- function(n,m){
  if((n<=0)||(n%%1 != 0)){
    stop("n tem que ser um natural")
  }
  if((m<=0)||(m%%1 != 0)){
    stop("m tem que ser um natural")
  }     
  vet = NULL
  i = 0
  repeat{
    if(i >= n){
      break
    }
    i = i + 1
    vet = c(vet,m*i)
  }
  return(vet)
}
multiplos_repeat(10,7)
##  [1]  7 14 21 28 35 42 49 56 63 70