Médio Rust
Iterador personalizado: números de Fibonacci
Enunciado
Implemente um iterador Fibonacci que gera a sequência de Fibonacci infinita (0, 1, 1, 2, 3, 5, ...). Use-o para coletar os 10 primeiros números em um Vec<u64> e imprimi-los.
Requisitos
- Crie uma struct
Fibonaccicom campos para os dois últimos valores. - Implemente o trait
IteratorparaFibonaccicomItem = u64. - Use
.take(10).collect::<Vec<u64>>()para obter os 10 primeiros. - Imprima o vetor resultante.
Código inicial
struct Fibonacci {
// defina os campos
}
impl Iterator for Fibonacci {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
// implemente
}
}
fn main() {
let fib = Fibonacci { /* inicialize */ };
let primeiros: Vec<u64> = fib.take(10).collect();
println!("{:?}", primeiros);
}
Saída esperada
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Ver dica
Mantenha dois campos: atual e proximo. No next, retorne atual e atualize os valores. Como é infinita, sempre retorne Some.
Mostrar solução
struct Fibonacci {
atual: u64,
proximo: u64,
}
impl Fibonacci {
fn new() -> Self {
Fibonacci { atual: 0, proximo: 1 }
}
}
impl Iterator for Fibonacci {
type Item = u64;
fn next(&mut self) -> Option<Self::Item> {
let valor = self.atual;
let novo = self.atual + self.proximo;
self.atual = self.proximo;
self.proximo = novo;
Some(valor) // sempre Some, pois é infinita
}
}
fn main() {
let fib = Fibonacci::new();
let primeiros: Vec<u64> = fib.take(10).collect();
println!("{:?}", primeiros);
}
Passo a passo
- Definimos a struct
Fibonaccicom dois camposu64:atual(o valor a ser retornado) eproximo(o próximo da sequência). - O método
newinicializa com 0 e 1, os dois primeiros números. - No
next, guardamosself.atualemvalor, que será retornado. - Calculamos
novo = self.atual + self.proximoe avançamos:self.atualrecebeself.proximo, eself.proximorecebenovo. - Retornamos
Some(valor). Como a sequência é infinita, nunca retornamosNone. - No
main, criamos o iterador e usamostake(10)para limitar a 10 elementos, depoiscollectparaVec<u64>.
Por que funciona
O trait Iterator só exige o método next. Ao implementá-lo, ganhamos todos os adaptadores (take, map, filter, etc.) gratuitamente. O iterador é preguiçoso: cada chamada a next calcula o próximo número sob demanda. take(10) cria um novo iterador que para após 10 elementos, e collect consome tudo.
Erros comuns
- Esquecer de atualizar os campos: se não atualizar
self.atualeself.proximo, o iterador retornaria sempre 0. Corrija com as atribuições na ordem correta. - Retornar
Noneprematuramente: como a sequência é infinita, não deve haver condição de parada. Se colocarif self.atual > 100 { None }, otake(10)ainda funcionaria, mas o iterador não seria mais infinito. - Overflow: após muitos elementos,
u64pode estourar. Para uso finito, não é problema; para infinito, useu128ou aceite o pânico.
Outra forma de resolver
Usar std::iter::successors para criar a sequência sem struct:
let fib = std::iter::successors(Some((0, 1)), |&(a, b)| Some((b, a + b))).map(|(a, _)| a);
let primeiros: Vec<u64> = fib.take(10).collect();
É preferível quando a lógica é simples e não precisa de estado nomeado.
Saída esperada
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]