Aula 4 – Estruturas condicionais, laços, rotinas e funções

Autor

Prof. Marcelo Ribeiro

Objetivo da Aula

Compreender e aplicar estruturas condicionais, laços de repetição, escrita de funções e organização de rotinas em R, com foco em problemas compatíveis com a formação em Estatística e Ciência de Dados.

1. Introdução

Nas aulas anteriores, estudamos objetos, vetores, matrizes, dataframes, listas, importação e exportação de dados. Agora avançaremos para um ponto central da programação: controlar o fluxo de execução do código.

Em termos práticos, esta aula responde a perguntas como:

  • como tomar decisões com base em condições lógicas;
  • como repetir automaticamente um conjunto de comandos;
  • como encapsular procedimentos em funções reutilizáveis;
  • como organizar scripts de forma mais limpa, reprodutível e profissional.
Comentário do professor

Uma das principais diferenças entre usar o R apenas como calculadora e utilizá-lo como linguagem de programação está justamente na capacidade de escrever códigos que decidem, repetem, automatizam e generalizam tarefas.

2. Estruturas condicionais

As estruturas condicionais permitem executar comandos somente quando determinada condição lógica é satisfeita.

2.1 A estrutura if

A estrutura mais básica é:

if (condicao) {
  # comandos executados se a condição for TRUE
}

Exemplo 1 – Verificando se um número é positivo

Execute este código
x <- 12

if (x > 0) {
  print("x é positivo")
}
[1] "x é positivo"
Comentário do professor

O bloco dentro das chaves será executado apenas se a condição x > 0 for verdadeira. Caso contrário, nada acontece.

Exemplo 2 – Ajustando uma variável caso ela seja negativa

Execute este código
saldo <- -35

if (saldo < 0) {
  saldo <- abs(saldo)
}

saldo
[1] 35
Ideia central

A estrutura if é útil quando queremos corrigir, filtrar, classificar ou acionar comandos a partir de uma regra lógica.

2.2 A estrutura if ... else

Quando queremos tratar também o caso em que a condição é falsa, usamos else.

if (condicao) {
  # comandos se TRUE
} else {
  # comandos se FALSE
}

Exemplo 3 – Verificando se um número é par ou ímpar

Execute este código
n <- 17

if (n %% 2 == 0) {
  print("n é par")
} else {
  print("n é ímpar")
}
[1] "n é ímpar"
Comentário do professor

O operador %% devolve o resto da divisão inteira. Assim, um número é par quando o resto da divisão por 2 é igual a zero.

Exemplo 4 – Classificação simples de desempenho

Execute este código
nota <- 7.3

if (nota >= 6) {
  print("Aprovado")
} else {
  print("Reprovado")
}
[1] "Aprovado"

2.3 A estrutura if ... else if ... else

Quando há mais de duas possibilidades, podemos encadear condições.

if (condicao1) {
  # bloco 1
} else if (condicao2) {
  # bloco 2
} else {
  # bloco 3
}

Exemplo 5 – Verificando se um número é positivo, negativo ou zero

Execute este código
z <- -4

if (z < 0) {
  print("z é negativo")
} else if (z == 0) {
  print("z é zero")
} else {
  print("z é positivo")
}
[1] "z é negativo"

Exemplo 6 – Faixas de rendimento em um contexto de análise de desempenho

Execute este código
nota_r <- 8.4

if (nota_r < 5) {
  categoria <- "baixo desempenho"
} else if (nota_r < 7) {
  categoria <- "desempenho intermediário"
} else if (nota_r < 9) {
  categoria <- "bom desempenho"
} else {
  categoria <- "excelente desempenho"
}

categoria
[1] "bom desempenho"
Comentário do professor

Quando as condições são avaliadas em sequência, o R interrompe o processo na primeira condição verdadeira.

2.4 A função ifelse()

A função ifelse() é útil quando queremos devolver um resultado de forma compacta.

ifelse(condicao, valor_se_true, valor_se_false)

Exemplo 7 – Classificação binária

Execute este código
idade <- 19

ifelse(idade >= 18, "maior de idade", "menor de idade")
[1] "maior de idade"

Exemplo 8 – Aplicação vetorial de ifelse()

Execute este código
notas <- c(4.2, 6.1, 8.7, 5.5, 9.0, 3.8)

situacao <- ifelse(notas >= 6, "aprovado", "reprovado")

situacao
[1] "reprovado" "aprovado"  "aprovado"  "reprovado" "aprovado"  "reprovado"
Comentário do professor

Uma grande vantagem do ifelse() é seu uso vetorial, isto é, ele consegue avaliar uma condição em todos os elementos de um vetor sem necessidade de escrever um laço explícito.

2.5 Cuidados importantes com condicionais

Atenção

Erros comuns em estruturas condicionais:

  • usar = em vez de == para comparação;
  • esquecer de fechar parênteses ou chaves;
  • tentar usar if com uma condição que produz mais de um valor lógico;
  • confundir & com && e | com ||.

Exemplo 9 – Diferença entre & e &&

Execute este código
x <- 5
y <- 12

x > 0 & y > 10
[1] TRUE
x > 0 && y > 10
[1] TRUE
Comentário do professor

No uso mais básico:

  • & faz comparação elemento a elemento;
  • && avalia apenas a primeira condição lógica de cada lado.

Em estruturas if, normalmente a condição deve resultar em um único valor lógico.

3. Laços de repetição

Laços permitem repetir um bloco de comandos várias vezes.

3.1 O laço for

A estrutura geral é:

for (contador in sequencia) {
  # comandos
}

Exemplo 10 – Imprimindo os números de 1 a 5

Execute este código
for (i in 1:5) {
  print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

Exemplo 11 – Calculando uma soma acumulada

Execute este código
soma <- 0

for (i in 1:10) {
  soma <- soma + i
}

soma
[1] 55
Comentário do professor

A variável soma funciona como acumuladora. Em cada iteração, ela recebe seu valor anterior somado ao valor atual de i.

Exemplo 12 – Criando um vetor com quadrados perfeitos

Execute este código
quadrados <- numeric(10)

for (i in 1:10) {
  quadrados[i] <- i^2
}

quadrados
 [1]   1   4   9  16  25  36  49  64  81 100
Boa prática

Sempre que possível, pré-alocar o objeto final com funções como numeric(), character() ou vector() costuma ser melhor do que ir concatenando valores com c() a cada repetição.

Exemplo 13 – Sequência não regular

Execute este código
valores <- c(-3, 0, 2.5, pi)

for (v in valores) {
  print(v)
}
[1] -3
[1] 0
[1] 2.5
[1] 3.141593

3.2 O laço while

O while executa um bloco enquanto uma condição for verdadeira.

while (condicao) {
  # comandos
}

Exemplo 14 – Somando múltiplos de 4 até 40

Execute este código
soma <- 0
i <- 4

while (i <= 40) {
  soma <- soma + i
  i <- i + 4
}

soma
[1] 220
Atenção

No while, o programador precisa garantir que a condição se torne falsa em algum momento. Caso contrário, o código pode entrar em laço infinito.

Exemplo 15 – Construindo uma sequência até ultrapassar um limite

Execute este código
valores <- NULL
x <- 1

while (x <= 100) {
  valores <- c(valores, x)
  x <- x * 2
}

valores
[1]  1  2  4  8 16 32 64
Comentário do professor

Esse tipo de estrutura é útil quando o número de iterações não está definido previamente, mas depende da evolução de uma condição.

3.3 break e next

Exemplo 16 – Interrompendo um laço com break

Execute este código
for (i in 1:10) {
  if (i > 6) {
    break
  }
  print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6

Exemplo 17 – Pulando uma iteração com next

Execute este código
for (i in 1:10) {
  if (i %% 2 == 0) {
    next
  }
  print(i)
}
[1] 1
[1] 3
[1] 5
[1] 7
[1] 9
Comentário do professor
  • break interrompe completamente o laço;
  • next pula apenas a iteração atual e continua nas seguintes.

4. Evitando laços quando possível

O R é uma linguagem fortemente vetorial. Em muitas situações, laços podem ser substituídos por estratégias mais simples e eficientes.

Exemplo 18 – Multiplicando todos os elementos de um vetor por 2

Execute este código
x <- c(3, 5, 7, 9)

2 * x
[1]  6 10 14 18

Exemplo 19 – Classificando valores com ifelse() sem for

Execute este código
notas <- c(4.5, 6.0, 7.2, 5.8, 9.1)

ifelse(notas >= 6, "A", "R")
[1] "R" "A" "A" "R" "A"

Exemplo 20 – Aplicando função por linha ou coluna com apply()

Execute este código
A <- matrix(1:12, nrow = 3, byrow = TRUE)

A
     [,1] [,2] [,3] [,4]
[1,]    1    2    3    4
[2,]    5    6    7    8
[3,]    9   10   11   12
apply(A, 1, sum)
[1] 10 26 42
apply(A, 2, mean)
[1] 5 6 7 8
Comentário do professor

Em apply(A, 1, sum), o valor 1 indica aplicação por linha.
Em apply(A, 2, mean), o valor 2 indica aplicação por coluna.

Exemplo 21 – lapply() e sapply()

Execute este código
lista_obj <- list(
  a = 1:5,
  b = c(2, 4, 8),
  c = c(10, 20)
)

lapply(lista_obj, length)
$a
[1] 5

$b
[1] 3

$c
[1] 2
sapply(lista_obj, length)
a b c 
5 3 2 
Comentário do professor

A família apply não substitui todos os laços, mas em muitas situações ela torna o código:

  • mais curto;
  • mais legível;
  • mais idiomático em R.

5. Escrevendo funções

Funções permitem encapsular tarefas e reutilizá-las com diferentes entradas.

A estrutura básica é:

nome_funcao <- function(parametros) {
  # corpo da função
  return(resultado)
}

5.1 Função sem parâmetro

Exemplo 22 – Mensagem de boas-vindas

Execute este código
boas_vindas <- function() {
  print("Bem-vindos à Aula 4 de R")
}

boas_vindas()
[1] "Bem-vindos à Aula 4 de R"

5.2 Função com um parâmetro

Exemplo 23 – Dobro de um número

Execute este código
dobra <- function(x) {
  y <- 2 * x
  return(y)
}

dobra(7)
[1] 14
dobra(2.5)
[1] 5

Exemplo 24 – Valor absoluto sem usar abs()

Execute este código
valor_absoluto_manual <- function(x) {
  if (x >= 0) {
    return(x)
  } else {
    return(-x)
  }
}

valor_absoluto_manual(-12)
[1] 12
valor_absoluto_manual(8)
[1] 8

5.3 Função com mais de um parâmetro

Exemplo 25 – Equação da reta

Execute este código
reta <- function(x, beta0, beta1) {
  y <- beta0 + beta1 * x
  return(y)
}

reta(x = 4, beta0 = 2, beta1 = 1.5)
[1] 8

A expressão matemática correspondente é dada por:

\[ y = \beta_0 + \beta_1 x \]

Exemplo 26 – Média de dois números

Execute este código
media2 <- function(a, b) {
  resultado <- (a + b) / 2
  return(resultado)
}

media2(8, 10)
[1] 9

A expressão matemática correspondente é:

\[ \bar{x} = \frac{a+b}{2} \]

5.4 Funções retornando mais de um resultado

Quando precisamos devolver várias saídas, podemos retornar uma lista.

Exemplo 27 – Soma, média e desvio-padrão

Execute este código
resumo_vetor <- function(v) {
  s <- sum(v)
  m <- mean(v)
  dp <- sd(v)
  
  return(list(
    soma = s,
    media = m,
    desvio_padrao = dp
  ))
}

x <- c(3, 7, 10, 12, 15)

resumo_vetor(x)
$soma
[1] 47

$media
[1] 9.4

$desvio_padrao
[1] 4.615192

As principais expressões envolvidas são:

\[ S = \sum_{i=1}^{n} x_i \]

\[ \bar{x} = \frac{1}{n} \sum_{i=1}^{n} x_i \]

\[ s = \sqrt{\frac{1}{n-1}\sum_{i=1}^{n}(x_i-\bar{x})^2} \]

Comentário do professor

Esse tipo de função é particularmente útil em rotinas de análise, pois reúne, em um único objeto, diferentes medidas calculadas a partir da mesma entrada.

5.5 Funções com validação de entrada

Em aplicações mais profissionais, é interessante verificar se a entrada é adequada.

Exemplo 28 – Média apenas para vetores numéricos

Execute este código
media_segura <- function(v) {
  if (!is.numeric(v)) {
    stop("A entrada deve ser numérica.")
  }
  
  return(mean(v))
}

media_segura(c(2, 4, 6, 8))
[1] 5
Atenção

A função stop() interrompe a execução e devolve uma mensagem de erro personalizada. Isso é muito útil para tornar funções mais robustas.

6. Escrevendo rotinas e scripts

Uma rotina ou script é um conjunto organizado de comandos salvo em um arquivo com extensão .R.

Comentário do professor

Em projetos reais, dificilmente tudo será digitado diretamente no console. O mais comum é construir um script com:

  • leitura dos dados;
  • tratamento;
  • análises;
  • gráficos;
  • exportação de resultados.

6.1 Exemplo de rotina simples

x <- round(runif(1000, min = -30, max = 50), 0)

media_desvio <- function(v) {
  m <- mean(v)
  dp <- sd(v)
  
  return(list(media = m, desvio_padrao = dp))
}

resultado <- media_desvio(x)

resultado$media
resultado$desvio_padrao
hist(x)

Exemplo 29 – Salvando uma rotina em arquivo

Suponha que você tenha criado um arquivo chamado rotina_aula4.R no diretório de trabalho.

Execute este código
source("rotina_aula4.R")
Comentário do professor

A função source() executa um script salvo em arquivo. Isso é extremamente útil para:

  • reaproveitar códigos;
  • automatizar etapas;
  • modularizar projetos.

6.2 Estrutura recomendada de um script analítico

Estrutura sugerida

Uma organização profissional mínima de um script pode seguir a ordem:

  1. limpeza do ambiente, quando necessário;
  2. definição do diretório ou projeto;
  3. leitura de dados;
  4. criação de funções auxiliares;
  5. processamento e análise;
  6. visualização dos resultados;
  7. exportação de objetos e tabelas.

Exemplo 30 – Mini fluxo organizado

# 1. Leitura dos dados
dados <- read.csv("base.csv", stringsAsFactors = FALSE)

# 2. Função auxiliar
top_media <- function(v) {
  mean(sort(v, decreasing = TRUE)[1:5])
}

# 3. Aplicação
resultado <- top_media(dados$nota_r)

# 4. Saída
resultado

7. Exemplos aplicados ao contexto de Estatística e Ciência de Dados

Nesta seção, vamos usar problemas mais próximos do perfil da disciplina.

7.1 Classificação de desempenho

Execute este código
nota_r <- c(5.4, 7.8, 9.1, 6.2, 4.7, 8.5)

classificacao <- ifelse(
  nota_r < 6, "insuficiente",
  ifelse(nota_r < 8, "adequado", "alto")
)

data.frame(nota_r, classificacao)

7.2 Seleção condicional em um dataframe

Execute este código
dados <- data.frame(
  aluno = c("Ana", "Bruno", "Carla", "Daniel", "Eva"),
  nota_r = c(8.7, 5.9, 9.1, 6.4, 7.8),
  projetos = c(3, 1, 5, 2, 4)
)

subset(dados, nota_r >= 7 & projetos >= 3)

7.3 Função para extrair os melhores desempenhos

Execute este código
top_media_notas <- function(df, nome_variavel, k = 3) {
  v <- df[[nome_variavel]]
  v_ordenado <- sort(v, decreasing = TRUE)
  media_top <- mean(v_ordenado[1:k])
  
  return(media_top)
}

top_media_notas(dados, "nota_r", k = 3)
[1] 8.533333

7.4 Construindo uma classificação com laço

Execute este código
notas <- c(4.5, 6.0, 8.2, 5.1, 9.0, 7.4)
classe <- character(length(notas))

for (i in 1:length(notas)) {
  if (notas[i] < 6) {
    classe[i] <- "R"
  } else if (notas[i] < 8) {
    classe[i] <- "A"
  } else {
    classe[i] <- "D"
  }
}

data.frame(notas, classe)
Comentário do professor

Apesar de esse exemplo poder ser resolvido com ifelse(), ele é didaticamente importante para mostrar como combinar laços e condicionais.

8. Erros comuns e boas práticas

Erros comuns
  • esquecer de inicializar uma variável acumuladora;
  • usar if quando a condição retorna um vetor lógico inteiro;
  • concatenar repetidamente objetos grandes dentro de laços sem necessidade;
  • criar funções sem nomes claros;
  • escrever scripts sem comentários e sem estrutura.
Boas práticas
  • use nomes de objetos informativos;
  • comente trechos importantes do código;
  • teste funções com exemplos simples antes de usá-las em rotinas maiores;
  • prefira soluções vetoriais quando fizer sentido;
  • salve scripts com nomes claros, como aula4_condicionais_funcoes.R.

9. Exercícios propostos

Exercício 1

Crie um script que receba um número x e:

  • informe se ele é negativo, zero ou positivo;
  • informe se ele é par ou ímpar;
  • devolva seu valor absoluto sem usar abs().

Exercício 2

Construa uma função chamada classifica_nota() que receba uma nota de 0 a 10 e devolva:

  • "insuficiente" se a nota for menor que 6;
  • "adequado" se a nota estiver entre 6 e 8, sem incluir 8;
  • "alto" se a nota for maior ou igual a 8.

Teste a função em pelo menos cinco valores diferentes.

Exercício 3

Use um laço for para construir um vetor contendo os 20 primeiros valores da sequência

\[ a_i = \frac{i}{i+1}, \quad i = 1, 2, \ldots, 20 \]

Depois:

  • mostre o vetor;
  • calcule sua média;
  • identifique o maior valor.

Exercício 4

Escreva uma função que receba um vetor numérico e devolva uma lista contendo:

  • o tamanho do vetor;
  • a soma;
  • a média;
  • a mediana;
  • o desvio-padrão;
  • o índice do maior valor.

Exercício 5

Considere o seguinte dataframe:

dados <- data.frame(
  id = 1:8,
  nota_r = c(8.2, 6.4, 9.5, 5.7, 7.8, 8.9, 4.9, 6.8),
  projetos = c(3, 1, 5, 0, 2, 4, 1, 2)
)

Faça o que se pede:

  • selecione apenas as observações com nota_r >= 7;
  • crie uma nova coluna chamada perfil, com:
  • "forte" se nota_r >= 8 e projetos >= 3;
  • "moderado" se nota_r >= 6;
  • "fraco" caso contrário;
  • ordene o resultado final por projetos e depois por nota_r, ambas em ordem decrescente.

Exercício 6

Escreva uma função chamada produto_escalar_manual() que receba dois vetores numéricos de mesmo tamanho e devolva o produto escalar entre eles, sem usar %*%.

O produto escalar entre dois vetores ( ) e ( ) de dimensão (n) é dado por:

\[ \mathbf{x} \cdot \mathbf{y} = \sum_{i=1}^{n} x_i y_i \]

Exercício 7

Escreva uma função que receba os coeficientes (a), (b) e (c) e devolva as raízes reais da equação do segundo grau

\[ ax^2 + bx + c = 0 \]

A função deve:

  • informar quando não existirem raízes reais;
  • devolver uma única raiz quando o discriminante for zero;
  • devolver duas raízes quando o discriminante for positivo.

Utilize o discriminante

\[ \Delta = b^2 - 4ac \]

e, quando ( ), as raízes

\[ x_1 = \frac{-b + \sqrt{\Delta}}{2a} \qquad \text{e} \qquad x_2 = \frac{-b - \sqrt{\Delta}}{2a} \]

Exercício 8

Crie um script com a seguinte estrutura:

  1. gere 500 valores aleatórios de uma normal com média 12 e desvio-padrão 3;
  2. escreva uma função que devolva média, variância e quartis;
  3. aplique essa função ao vetor gerado;
  4. construa um histograma;
  5. salve esse script com um nome apropriado.

Lembre-se de que, para uma variável aleatória normal,

\[ X \sim N(\mu, \sigma^2) \]

onde, neste exercício,

\[ \mu = 12 \qquad \text{e} \qquad \sigma = 3 \]

Comentário do professor

Os exercícios desta aula foram pensados para consolidar o raciocínio de programação em R em um nível compatível com estudantes de Estatística e Ciência de Dados. O objetivo não é apenas “fazer funcionar”, mas escrever códigos organizados, interpretáveis e reutilizáveis.

Tarefa do aluno

Refaça, no seu próprio script, pelo menos três exemplos desta aula e resolva os exercícios propostos sem copiar diretamente os blocos já apresentados. O objetivo é treinar a autonomia na escrita de código.