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á avaliada 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] "2022-07-12 09:23:00 -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 parar a execução da função e devolver 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] NANa 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 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] NATambé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 iteraçã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.8125O 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ão1:numero_de_colunascria uma sequência de números de 1 a 11.A expressão
coluna in 1:numero_de_colunasindica que o valor decolunaserá 1 na primeira iteração, 2 na segunda iteração, 3 na terceira e assim por diante.O código dentro do
fornão é retornado para o usuário ao fim de cada iteração. Por isso, para ver os resultados no Console, usamos a funçãoprint().
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.812500Assim 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
forpara imprimir as médias acumuladas do vetora, 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.