3.12 Controle de Fluxo

Como toda boa linguagem de programação, o R possui estruturas de if, else, for e while. Esses controles de fluxo são muito importantes na hora de programar, pois nos permitem manipular de modo eficiente as ações do computador.

A seguir, explicaremos para que servem e como utilizar cada uma dessas estruturas.

3.12.1 Condicionamento: if e else

As estruturas if e else servem para executarmos um código apenas se uma condição (teste lógico) for satisfeita.

No código abaixo, a função Sys.time(), que retorna a data/hora no momento da execução, só será avlaiada se o objeto x for igual a 1.

# Não vai executar a função Sys.time()
x <- 2

if (x == 1) {         
  Sys.time()
}

# Vai executar a função Sys.time()
x <- 1

if (x == 1) {         
  Sys.time()
}
## [1] "2021-07-14 14:27:19 -03"

O R só vai executar o que está na expressão dentro das chaves {} se a expressão que estiver dentro dos parênteses () retornar TRUE. Veja outro exemplo:

# Vai fazer  a soma
x <- c(1, 3, 10, 15)

if (class(x) == "numeric") {
  sum(x)
}
## [1] 29

# Não vai fazer a soma
x <- c("a", "b", "c")

if (class(x) == "numeric") {
  sum(x)
}

Nesse exemplo, a soma só é executada se a classe do objeto x for numérica, isto é, se x for um vetor de números. Essa verificação poderia ser colocada dentro de uma função para evitarmos que ela retorne um erro.

minha_soma <- function(x, y) {
  if (class(x) == "numeric" & class(y) == "numeric") {
     x + y
  }
}

# Retorna a soma
minha_soma(1, 2)
## [1] 3

# Não retorna nada
minha_soma("a", "b")

Nesses casos, é muito comum o uso das funções return() e stop() para, respectivamente, retornar um resultado antecipadamente ou para a execução da função e devolver ao usuário uma mensagem de erro personalizada.

Um exemplo usando return().

# Devolvendo um resultado antecipadamente
minha_soma_NA <- function(x, y) {
  if (class(x) == "numeric" & class(y) == "numeric") {
     soma <- x + y
     return(soma)
  }
  
  NA
}

# Retorna a soma
minha_soma_NA(1, 2)
## [1] 3

# Retorna NA
minha_soma_NA("a", "b")
## [1] NA

# Retorna NA
minha_soma_NA(1, "b")
## [1] NA

Na função minha_soma_NA(), a soma só é calculada e retornada se x e y forem numéricos. Caso pelo menos um dos dois não seja, o código dentro do if não é executado e o valor retornado é o NA.

Agora, usando stop().

# Agora, devolvendo um erro
minha_soma_erro <- function(x, y) {
  if (class(x) != "numeric" | class(y) != "numeric") {
    stop("A classe dos objetos x e y deve ser numérica.")
  }
  
  x + y
}

# Retorna a soma
minha_soma_erro(1, 2)
## [1] 3

# Retorna erro
minha_soma_erro("a", "b")
## Error in minha_soma_erro("a", "b"): A classe dos objetos x e y deve ser numérica.

# Retorna erro
minha_soma_erro(1, "b")
## Error in minha_soma_erro(1, "b"): A classe dos objetos x e y deve ser numérica.

Na função minha_soma_erro(), testamos no if se a classe de x ou a classe de y é diferente de numeric, isto é, se pelo menos um dos dois não é um número. Se esse teste retornar TRUE, a função para a sua execução e devolve para o usuário a seguinte mensagem de erro: “A classe dos objetos x e y deve ser numérica.”. Se o teste retorna FALSE, a soma é realizada e seu resultado nos é retornado.

O else funciona como uma extensão do if, dando uma alternativa caso o teste executado seja falso.

# Vai fazer  a soma
x <- c(1, 3, 10, 15)

if (class(x) == "numeric") {
  sum(x)
} else {
  NA
}
## [1] 29

# Vai retornar NA
x <- c(1, 3, 10, "15")

if (class(x) == "numeric") {
  sum(x)
} else {
  NA
}
## [1] NA

Também podemos usar o else para encadear vários ifs. Teste o código abaixo com valores positivos e negativos para x.

x <- 0

if(x < 0) {
  
  "negativo"
  
} else if(x == 0) {
  
  "neutro"
  
} else if(x > 0) {
  
  "positivo"
}
## [1] "neutro"

Repare que o if no último else poderia ser omitido.

x <- 0

if(x < 0) {
  
  "negativo"
  
} else if(x == 0) {
  
  "neutro"
  
} else {
  
  "positivo"
}
## [1] "neutro"

3.12.2 Iteradores: for e while

O for pode ser utilizado para fazer os famosos loopings de programação, isto é, repetir uma mesma tarefa para um conjunto de valores diferentes. Cada repetição é chamada de iteração e o objeto que muda de valor em cada interação é chamado de iterador.

numero_de_colunas <- ncol(mtcars)

for (coluna in 1:numero_de_colunas) {
  media <- mean(mtcars[,coluna])
  
  print(media)
}
## [1] 20.09062
## [1] 6.1875
## [1] 230.7219
## [1] 146.6875
## [1] 3.596563
## [1] 3.21725
## [1] 17.84875
## [1] 0.4375
## [1] 0.40625
## [1] 3.6875
## [1] 2.8125

O código acima vai calcular a média de cada coluna do data frame mtcars. Alguns pontos importantes:

  • No exemplo, temos 11 iterações e o objeto coluna é o iterador.

  • Como numero_de_colunas é igual a 11, a expressão 1:numero_de_colunas cria uma sequência de números de 1 a 11.

  • A expressão coluna in 1:numero_de_colunas indica que o valor de coluna será 1 na primeira iteração, 2 na segunda iteração, 3 na terceira e assim por diante.

  • O código dentro do for não é retornado para o usuário ao fim de cada iteração. Por isso, para ver os resultados no Console, usamos a função print().

Também podemos salvar as médias em um vetor.

numero_de_colunas <- ncol(mtcars)

# Antes, criamos um vetor vazio.
medias <- c()

for (coluna in 1:numero_de_colunas) {
  medias[coluna] <- mean(mtcars[,coluna])
}

medias
##  [1]  20.090625   6.187500 230.721875 146.687500   3.596563   3.217250
##  [7]  17.848750   0.437500   0.406250   3.687500   2.812500

Assim como o for, o while também é um iterador.

O código a seguir irá imprimir na tela o valor de i enquanto este objeto for menor que 3. No momento em que a condição dentro das chaves {} não for mais respeitada, o processo será interrompido.

i <- 1

while (i < 3){
  print(i)
  i <-  i + 1
}
## [1] 1
## [1] 2

É importante que o valor de i seja atualizado em cada interação, caso contrário a função entrará em um loop infinito. Por isso fazemos i <- i + 1 após o print.

Exercícios

1. Por que o código abaixo retorna erro? Arrume o código para retornar o valor TRUE.

x <- 4
if(x = 4) {
  TRUE
}

2. Usando if e else, escreva um código que retorne a string "número" caso o valor seja da classe numeric ou integer; a string "palavra" caso o valor seja da classe character; e NA caso contrário.

3. Usando apenas for e a função length(), construa uma função que calcule a média de um vetor número qualquer. Construa uma condição para a função retornar NULL caso o vetor não seja numérico.

4. Utilize o vetor a para resolver as questões a seguir:

a <- c(10, 3, 5, -1, 3, -4, 8, 9, -10)
  • a. Utilize o for para imprimir as médias acumuladas do vetor a, isto é, primeiro vamos imprimir 10, depois a média entre 10 e 3, depois a média entre 10, 3 e 5 e assim por diante.

  • b. Adapte o laço que você fez no item anterior para ignorar os valores negativos, isto é, em caso de valor negativo, o laço não deve calcular a média e não imprimir nada.


Curso-R