Das Iterator-Konzept
Ein Iterator liefert nacheinander Elemente (next() gibt Some(x) oder am Ende None). Iterator-Ketten sind lazy: Es passiert nichts, bis ein Verbraucher (collect, sum, for) sie abruft. Dabei sind sie so schnell wie handgeschriebene Schleifen.
fn main() {
let v = vec![1, 2, 3];
let mut it = v.iter();
println!("{:?} {:?} {:?} {:?}", it.next(), it.next(), it.next(), it.next());
let quadrate: Vec<i32> = v.iter().map(|x| x * x).collect();
let gerade: Vec<&i32> = v.iter().filter(|x| **x % 2 == 0).collect();
let summe: i32 = v.iter().sum();
println!("{quadrate:?} {gerade:?} {summe}");
}Some(1) Some(2) Some(3) None [1, 4, 9] [2] 6
Drei Arten, über eine Collection zu iterieren: iter() (leiht &T), iter_mut() (leiht &mut T), into_iter() (verbraucht, liefert T).
Adapter
fn main() {
let zahlen = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let r: Vec<i32> = zahlen.iter().copied().filter(|x| x % 2 == 0).map(|x| x * 10).collect();
println!("{r:?}");
println!("{:?}", zahlen.iter().take(3).collect::<Vec<_>>());
println!("{:?}", zahlen.iter().skip(7).collect::<Vec<_>>());
println!("{:?}", zahlen.iter().step_by(4).collect::<Vec<_>>());
println!("{:?}", zahlen.iter().take_while(|&&x| x < 4).collect::<Vec<_>>());
println!("{:?}", zahlen.iter().skip_while(|&&x| x < 8).collect::<Vec<_>>());
println!("{:?}", zahlen.iter().rev().take(2).collect::<Vec<_>>());
for (i, x) in ["a", "b", "c"].iter().enumerate() {
print!("{i}:{x} ");
}
println!();
let namen = ["Mia", "Tom"];
let alter = [17, 19];
let paare: Vec<(&str, i32)> = namen.iter().copied().zip(alter.iter().copied()).collect();
println!("{paare:?}");
let verschachtelt = vec![vec![1, 2], vec![3], vec![]];
println!("{:?}", verschachtelt.into_iter().flatten().collect::<Vec<_>>());
println!("{:?}", ["ab", "cd"].iter().flat_map(|s| s.chars()).collect::<String>());
println!("{:?}", [1, 2, 3].iter().chain([4, 5].iter()).count());
}[20, 40, 60, 80, 100]
[1, 2, 3]
[8, 9, 10]
[1, 5, 9]
[1, 2, 3]
[8, 9, 10]
[10, 9]
0:a 1:b 2:c
[("Mia", 17), ("Tom", 19)]
[1, 2, 3]
"abcd"
5Verbraucher
fn main() {
let z = [3, 8, 1, 9, 4];
println!("{} {}", z.iter().sum::<i32>(), z.iter().product::<i32>());
println!("{:?} {:?}", z.iter().max(), z.iter().min());
println!("{:?}", z.iter().max_by_key(|&&x| (x as i32 - 5).abs()));
println!("{} {}", z.iter().any(|&x| x > 8), z.iter().all(|&x| x > 1));
println!("{:?} {:?}", z.iter().find(|&&x| x > 5), z.iter().position(|&x| x == 1));
println!("{}", z.iter().fold(0, |acc, x| acc * 10 + x));
let (klein, gross): (Vec<i32>, Vec<i32>) = z.iter().partition(|&&x| x < 5);
println!("{klein:?} {gross:?}");
let texte = ["4", "x", "7"];
let zahlen: Vec<i32> = texte.iter().filter_map(|s| s.parse().ok()).collect();
println!("{zahlen:?}");
let ergebnis: Result<Vec<i32>, _> = texte.iter().map(|s| s.parse::<i32>()).collect();
println!("{}", ergebnis.is_err());
let alle: Result<Vec<i32>, _> = ["1", "2"].iter().map(|s| s.parse::<i32>()).collect();
println!("{alle:?}");
}25 864 Some(9) Some(1) Some(9) true false Some(8) Some(2) 38194 [3, 1, 4] [8, 9] [4, 7] true Ok([1, 2])
collect kann in viele Typen sammeln: Vec, String, HashMap, HashSet und sogar Result<Vec<_>, _> (bricht beim ersten Fehler ab).
Eigene Iteratoren
Implementiere das Trait Iterator und du bekommst alle Adapter geschenkt:
struct Fibonacci { a: u64, b: u64 }
impl Iterator for Fibonacci {
type Item = u64;
fn next(&mut self) -> Option<u64> {
let r = self.a;
self.a = self.b;
self.b += r;
Some(r)
}
}
fn main() {
let f = Fibonacci { a: 0, b: 1 };
let v: Vec<u64> = f.take(10).collect();
println!("{v:?}");
let s: u64 = Fibonacci { a: 0, b: 1 }.take_while(|&x| x < 100).filter(|x| x % 2 == 0).sum();
println!("{s}");
println!("{:?}", (1..=5).map(|x| x * x).rev().collect::<Vec<_>>());
println!("{:?}", std::iter::repeat("ab").take(3).collect::<String>());
println!("{:?}", std::iter::successors(Some(1), |&x| if x < 100 { Some(x * 3) } else { None }).collect::<Vec<_>>());
}[0, 1, 1, 2, 3, 5, 8, 13, 21, 34] 44 [25, 16, 9, 4, 1] "ababab" [1, 3, 9, 27, 81, 243]
Closures als Parameter und Rückgabe
fn anwenden<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(x) }
fn macher(faktor: i32) -> impl Fn(i32) -> i32 { move |x| x * faktor }
fn boxed(op: &str) -> Box<dyn Fn(i32, i32) -> i32> {
match op {
"+" => Box::new(|a, b| a + b),
_ => Box::new(|a, b| a * b),
}
}
fn main() {
println!("{}", anwenden(|x| x + 1, 5));
let verdopple = macher(2);
println!("{} {}", verdopple(21), boxed("+")(3, 4));
let mut woerter = vec!["kiwi", "apfel", "fig"];
woerter.sort_by_key(|w| w.len());
println!("{woerter:?}");
let mut zaehler = 0;
let mut inc = || { zaehler += 1; zaehler };
inc(); inc();
println!("{}", zaehler);
}6 42 7 ["fig", "kiwi", "apfel"] 2
Merke
- Iteratoren sind lazy; erst
collect,sum,for... führen sie aus iter()leiht,iter_mut()leiht veränderbar,into_iter()verbraucht- Adapter:
map,filter,take,skip,zip,enumerate,flatten,rev - Verbraucher:
sum,fold,any,all,find,position,collect,partition - Eigene Iteratoren:
Iteratormitnext()implementieren
Aufgabe
Bestimme mit einer Iterator-Kette die Summe der Quadrate aller ungeraden Zahlen von 1 bis 20.