Swift: Intervalos e Sequências do Swift
Um intervalo é como um segmento em uma reta numérica — inteiros consecutivos de A até B. Esta lição aborda como criar, usar e personalizar todos os tipos de intervalo e sequência no Swift.
1. O Que Você Vai Aprender
- Criar e percorrer intervalos com
...e..< - Avançar por valores com passos personalizados usando stride
- Construir sequências personalizadas em conformidade com o protocolo Sequence
- Emparelhar duas sequências para percorrimento paralelo com zip
- Aplicar intervalos em instruções switch e fatiamento de array na prática
2. Uma História Real de um Organizador de Eventos
(1) Problema: Gerar manualmente 200 números de assento — Excel travou
Charlie está organizando uma conferência de tecnologia para 200 pessoas. Ele precisa gerar números de assento: fileiras A a T (20 fileiras), 10 assentos por fileira, atribuir participantes aos assentos e produzir um mapa de lugares. Ele começou arrastando a alça de preenchimento no Excel, mas toda vez que adicionava um assento VIP, precisava reajustar toda a planilha:
let seatA1 = "A1"
let seatA2 = "A2"
// ... 200 variables total
No assento #150, o Excel travou. Charlie percebeu: ele precisava de geração programática de números de assento.
(2) A Solução com Range e Stride
let rows = "ABCDEFGHIJKLMNOPQRST"
let seatsPerRow = 10
for row in rows {
for seat in 1...seatsPerRow {
print("\(row)\(seat)", terminator: " ")
}
print()
}
(3) Resultado: 5 horas → 3 segundos
| Métrica | Arrastando no Excel | Geração com Range no Swift |
|---|---|---|
| 200 assentos | 5 horas | 3 segundos |
| Mudar layout dos assentos | Arrastar novamente 10 minutos | Mudar um número |
| Assentos espaçados | Consulta manual | stride gera diretamente |
| Taxa de erro | Alta | 0 |
3. Range e ClosedRange
Range (semiaberto ..<) inclui o valor inicial mas exclui o final. ClosedRange (fechado ...) inclui ambos os extremos.
graph LR
A["1...5"] --> B[1]
A --> C[2]
A --> D[3]
A --> E[4]
A --> F[5]
G["1..<5"] --> H[1]
G --> I[2]
G --> J[3]
G --> K[4]
| Tipo de Intervalo | Sintaxe | Inclui | Exemplo de Resultado |
|---|---|---|---|
| ClosedRange | a...b |
a, a+1, ..., b | 1...3 -> 1,2,3 |
| Range | a..<b |
a, a+1, ..., b-1 | 1..<3 -> 1,2 |
| PartialFrom | a... |
a até o infinito | Fatiamento de array array[2...] |
| PartialThrough | ...b |
... até b | Fatiamento de array array[...2] |
(1) Criando e Percorrendo Intervalos
// Closed range: includes 1 through 5
let closed = 1...5
for i in closed {
print(i, terminator: " ")
}
print()
// Half-open range: includes 1 through 4
let halfOpen = 1..<5
for i in halfOpen {
print(i, terminator: " ")
}
print()
// Reverse traversal
for i in (1...5).reversed() {
print(i, terminator: " ")
}
print()
(2) Intervalos no Fatiamento de Array
let numbers = [10, 20, 30, 40, 50, 60, 70]
let firstThree = numbers[0..<3]
print(firstThree)
let fromSecond = numbers[2...]
print(fromSecond)
let firstFour = numbers[...3]
print(firstFour)
▶ Exemplo: Classificação de Notas com Correspondência de Intervalo
// ============================================
// Using ranges in switch for grade classification
// ============================================
let scores = [95, 82, 67, 54, 43, 78, 91]
for score in scores {
let grade: String
switch score {
case 90...100:
grade = "A"
case 80..<90:
grade = "B"
case 70..<80:
grade = "C"
case 60..<70:
grade = "D"
case 0..<60:
grade = "F"
default:
grade = "Invalid"
}
print("Score \(score): Grade \(grade)")
}
Saída:
TEXT 📖 Somente leituraScore 95: Grade A Score 82: Grade B Score 67: Grade D Score 54: Grade F Score 43: Grade F Score 78: Grade C Score 91: Grade A
4. Stride e Sequências Personalizadas
Stride permite percorrer com um valor de passo personalizado em vez de incrementar de 1 em 1. Sequências personalizadas permitem definir sua própria lógica de iteração.
graph TB
A[Sequence] --> B["stride(from: 0, to: 10, by: 2)"]
A --> C["stride(from: 10, through: 0, by: -2)"]
B --> D["0, 2, 4, 6, 8"]
C --> E["10, 8, 6, 4, 2, 0"]
| Função | Tipo de Intervalo | Extremo Inclusivo | Exemplo |
|---|---|---|---|
| stride(from:to:by:) | Semiaberto | Não | stride(from:0, to:10, by:3) -> 0,3,6,9 |
| stride(from:through:by:) | Fechado | Sim | stride(from:0, through:10, by:3) -> 0,3,6,9 |
(1) Uso Básico do Stride
// 0 to 9, step 2
for i in stride(from: 0, to: 10, by: 2) {
print(i, terminator: " ")
}
print()
// 10 to 0, step -3
for i in stride(from: 10, through: 0, by: -3) {
print(i, terminator: " ")
}
print()
// Floating-point stepping
for temp in stride(from: 0.0, to: 1.0, by: 0.25) {
print("\(Int(temp * 100))%", terminator: " ")
}
print()
(2) Sequências Personalizadas
struct FibonacciSequence: Sequence {
let count: Int
func makeIterator() -> FibonacciIterator {
return FibonacciIterator(count: count)
}
}
struct FibonacciIterator: IteratorProtocol {
let count: Int
var current = 0
var nextValue = 1
var index = 0
mutating func next() -> Int? {
guard index < count else { return nil }
defer { index += 1 }
let result = current
current = nextValue
nextValue = result + current
return result
}
}
let fib = FibonacciSequence(count: 10)
for num in fib {
print(num, terminator: " ")
}
print()
▶ Exemplo: Gerador de Tabela de Conversão de Temperatura
// ============================================
// Generate temperature conversion table with stride
// ============================================
print("Celsius\tFahrenheit")
print("-----------------")
for celsius in stride(from: 0.0, through: 100.0, by: 10.0) {
let fahrenheit = celsius * 9 / 5 + 32
print("\(Int(celsius))\t\(Int(fahrenheit))")
}
print()
print("Fahrenheit\tCelsius")
print("-------------------")
for f in stride(from: 212.0, through: 32.0, by: -20.0) {
let c = (f - 32) * 5 / 9
print("\(Int(f))\t\t\(Int(c))")
}
Saída:
TEXT 📖 Somente leituraCelsius Fahrenheit ----------------- 0 32 10 50 20 68 ... (linhas intermediárias omitidas) 100 212 Fahrenheit Celsius ------------------- 212 100 192 88 ... (linhas intermediárias omitidas) 32 0
5. zip e Combinação de Sequências
zip emparelha duas sequências por índice, produzindo uma sequência de tuplas. Ele para automaticamente quando qualquer uma das sequências termina.
| Característica | Descrição |
|---|---|
| Entrada | Duas sequências |
| Saída | Sequência de tuplas (Elemento1, Elemento2) |
| Condição de parada | A sequência que terminar primeiro |
| Usos comuns | Percorrimento paralelo, emparelhamento de dados, geração de índices |
(1) Uso Básico do zip
let names = ["Alice", "Bob", "Charlie", "Diana"]
let scores = [88, 92, 75, 95]
for (name, score) in zip(names, scores) {
print("\(name): \(score)")
}
let short = ["A", "B", "C"]
let long = [1, 2, 3, 4, 5]
for pair in zip(short, long) {
print(pair)
}
(2) zip + Range para Geração de Índices
let fruits = ["Apple", "Banana", "Orange", "Grape"]
for (index, fruit) in zip(1..., fruits) {
print("\(index). \(fruit)")
}
▶ Exemplo: Sistema de Atribuição de Assentos
// ============================================
// Assign attendees to seats using zip and ranges
// ============================================
let attendees = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
let rows = ["A", "B", "C", "D"]
let seatsPerRow = 4
var allSeats: [String] = []
for row in rows {
for seat in 1...seatsPerRow {
allSeats.append("\(row)\(seat)")
}
}
print("=== Seat Assignments ===")
for (attendee, seat) in zip(attendees, allSeats) {
print("\(seat): \(attendee)")
}
let vipCount = 2
let vipSeats = allSeats.prefix(vipCount)
let vipAttendees = attendees.prefix(vipCount)
print("\n=== VIP Section ===")
for (person, seat) in zip(vipAttendees, vipSeats) {
print("\(seat): \(person)")
}
Saída:
TEXT 📖 Somente leitura=== Atribuições de Assentos === A1: Alice A2: Bob A3: Charlie A4: Diana B1: Eve B2: Frank === Seção VIP === A1: Alice A2: Bob
6. Exemplo Completo: Mapa de Assentos de Prova e Análise de Notas
// ============================================
// Exam seating chart and grade analysis
// Combining Range / Stride / zip / Sequence
// ============================================
import Foundation
let examRows = 5
let cols = 4
print("=== Exam Seating Chart ===")
for row in 0..<examRows {
let rowLabel = Character(UnicodeScalar(65 + row)!)
for col in 1...cols {
print("\(rowLabel)\(col)", terminator: "\t")
}
print()
}
let students = ["Alice", "Bob", "Charlie", "Diana", "Eve",
"Frank", "Grace", "Henry", "Ivy", "Jack",
"Kevin", "Leo", "Mia", "Noah", "Olivia",
"Paul", "Quinn", "Rose", "Sam", "Tina"]
let examSeats = examRows * cols
let assignedIndices = stride(from: 0, to: min(students.count, examSeats), by: 1)
print("\n=== Seat Assignments ===")
for (index, studentIndex) in zip(assignedIndices, 0..<students.count) {
let row = index / cols
let col = index % cols + 1
let rowLabel = Character(UnicodeScalar(65 + row)!)
print("\(rowLabel)\(col): \(students[studentIndex])")
}
print("\n=== Score Report ===")
var scores: [String: Int] = [:]
for student in students.prefix(examSeats) {
scores[student] = Int.random(in: 40...100)
}
let gradeRanges: [ClosedRange<Int>: String] = [
90...100: "A",
80...89: "B",
70...79: "C",
60...69: "D",
0...59: "F"
]
var gradeCount: [String: Int] = ["A": 0, "B": 0, "C": 0, "D": 0, "F": 0]
for (student, score) in scores.sorted(by: { $0.key < $1.key }) {
var grade = "F"
for (range, g) in gradeRanges {
if range.contains(score) {
grade = g
break
}
}
gradeCount[grade]! += 1
print("\(student): \(score) -- \(grade)")
}
print("\n=== Grade Distribution ===")
for grade in ["A", "B", "C", "D", "F"] {
let count = gradeCount[grade]!
let bar = String(repeating: "#", count: count)
print("\(grade): \(bar) (\(count))")
}
Saída:
TEXT 📖 Somente leitura=== Mapa de Assentos da Prova === A1 A2 A3 A4 B1 B2 B3 B4 C1 C2 C3 C4 D1 D2 D3 D4 E1 E2 E3 E4 === Atribuições de Assentos === A1: Alice A2: Bob ... (linhas intermediárias omitidas) E4: Tina === Relatório de Notas === Alice: 82 -- B Bob: 67 -- D ... (linhas intermediárias omitidas) === Distribuição de Conceitos === A: ### (3) B: ##### (5) C: #### (4) D: ### (3) F: # (1)
❓ Perguntas Frequentes
P: Qual é a diferença entre
...e..<? R:a...binclui b (intervalo fechado).a..<bexclui b (intervalo semiaberto). Por exemplo, 1...3 percorre 1,2,3; 1..<3 percorre 1,2. P: Os intervalos podem ser usados com tipos não inteiros? R: Sim. Qualquer tipo em conformidade com Comparable funciona, como Double ou Character: 1.0...5.0 ou "a"..."z". Note que intervalos Double não podem ser usados com for-in (ambiguidade de passo). P: O parâmetro by do stride pode ser negativo? R: Sim. Um valor negativo cria um percorrimento descendente. Lembre-se de inverter a ordem de from e to/through — maior primeiro, menor depois. P: Qual é o comprimento da sequência retornada por zip? R: O comprimento da sequência de entrada mais curta. zip para quando qualquer sequência termina; ele não preenche com nil. P: Como verificar se um valor está dentro de um intervalo? R: Use o método contains():(1...10).contains(5)retorna true. Ou corresponda em um switch:case 1...10:. P: O fatiamento de array comarray[2...]copia o array? R: Não. O fatiamento de array retorna um ArraySlice que compartilha a memória do array original — nenhuma cópia é feita. O slice tem seu próprio deslocamento de índice inicial.
📖 Resumo
- Intervalo fechado
...e intervalo semiaberto..<são os tipos fundamentais de intervalo - Intervalos podem ser usados diretamente no case do switch para correspondência de intervalo
- stride permite valores de passo personalizados e suporta percorrimento descendente e com ponto flutuante
- Sequence e IteratorProtocol personalizados permitem definir sua própria lógica de iteração
- zip emparelha duas sequências em paralelo, parando quando a mais curta termina
- O fatiamento de array com
[2...]e[..<3]fornece acesso eficiente a sub-arrays
📝 Exercícios
- Iniciante: Use 1...10 para imprimir a 3ª linha de uma tabuada (31 até 310), formatada como "3 x 1 = 3".
- Intermediário: Use stride para gerar números pares de 100 até 0 com passo de -2 e calcule sua soma.
- Desafio: Crie uma sequência personalizada SequenciaDiasSemana que começa de um dia da semana dado (ex.: "Qua") e itera indefinidamente por ["Seg", "Ter", "Qua", "Qui", "Sex", "Sáb", "Dom"]. Use zip para emparelhar com os primeiros 14 inteiros e imprimir "Dia 1: Qua", "Dia 2: Qui"...