Il peut être utile de parcourir plusieurs variables à la fois, en chevauchant ( slice::windows
), ou non ( slice::chunks
).
Cela ne fonctionne que pour les tranches; est-il possible de le faire pour les itérateurs, en utilisant des tuples pour plus de commodité?
Quelque chose comme ce qui suit pourrait être écrit:
for (prev, next) in some_iter.windows(2) {
...
}
Sinon, pourrait-il être implémenté comme un trait sur les itérateurs existants?
TL; DR: La meilleure façon d'avoir chunks
et windows
sur un itérateur/collection arbitraire est de commencer par collect
dans un Vec
et itérer sur que .
La syntaxe exacte demandée est impossible dans Rust.
Le problème est que dans Rust, la signature d'une fonction dépend des types , et non des valeurs , et bien que la typologie dépendante existe, peu de langages l'implémentent (c'est difficile).
C'est pourquoi chunks
et windows
renvoient une sous-tranche en passant; le nombre d'éléments dans un &[T]
ne fait pas partie du type et peut donc être décidé au moment de l'exécution.
Imaginons que vous ayez demandé: for slice in some_iter.windows(2)
à la place.
Où le stockage sur lequel cette tranche se trouverait-il?
Il ne peut pas vivre:
LinkedList
n'a pas de stockage contiguIterator::Item
, il n'y a pas de durée de vie disponibleDonc, malheureusement, les tranches ne peuvent être utilisées que lorsque le stockage de sauvegarde est une tranche.
Si les allocations dynamiques sont acceptées, il est possible d'utiliser Vec<Iterator::Item>
Comme Item
de l'itérateur de segmentation.
struct Chunks<I: Iterator> {
elements: Vec<<I as Iterator>::Item>,
underlying: I,
}
impl<I: Iterator> Chunks<I> {
fn new(iterator: I, size: usize) -> Chunks<I> {
assert!(size > 0);
let mut result = Chunks {
underlying: iterator, elements: Vec::with_capacity(size)
};
result.refill(size);
result
}
fn refill(&mut self, size: usize) {
assert!(self.elements.is_empty());
for _ in 0..size {
match self.underlying.next() {
Some(item) => self.elements.Push(item),
None => break,
}
}
}
}
impl<I: Iterator> Iterator for Chunks<I> {
type Item = Vec<<I as Iterator>::Item>;
fn next(&mut self) -> Option<Self::Item> {
if self.elements.is_empty() {
return None;
}
let new_elements = Vec::with_capacity(self.elements.len());
let result = std::mem::replace(&mut self.elements, new_elements);
self.refill(result.len());
Some(result)
}
}
fn main() {
let v = vec!(1, 2, 3, 4, 5);
for slice in Chunks::new(v.iter(), 2) {
println!("{:?}", slice);
}
}
Reviendra:
[1, 2] [3, 4] [5]
Le lecteur avisé se rendra compte que je suis passé subrepticement de windows
à chunks
.
windows
est plus difficile, car il renvoie plusieurs fois le même élément, ce qui nécessite que l'élément soit Clone
. De plus, comme il doit renvoyer un Vec
complet à chaque fois, il devra en interne conserver un Vec<Vec<Iterator::Item>>
.
Ceci est laissé comme exercice au lecteur.
Enfin, une note sur performance: toutes ces allocations vont faire mal (surtout dans le cas windows
).
La meilleure stratégie d'allocation consiste généralement à allouer un seul morceau de mémoire, puis à en vivre (à moins que la quantité ne soit vraiment énorme, auquel cas le streaming est requis).
Cela s'appelle collect::<Vec<_>>()
dans Rust.
Et puisque Vec
a une méthode chunks
et windows
(grâce à l'implémentation de Deref<Target=[T]>
), Vous pouvez alors l'utiliser à la place:
for slice in v.iter().collect::<Vec<_>>().chunks(2) {
println!("{:?}", slice);
}
for slice in v.iter().collect::<Vec<_>>().windows(2) {
println!("{:?}", slice);
}
Parfois, les meilleures solutions sont les plus simples.
Il est possible de prendre des morceaux d'un itérateur en utilisant Itertools::tuples
, jusqu'à 4 tuples:
extern crate itertools;
use itertools::Itertools;
fn main() {
let some_iter = vec![1, 2, 3, 4, 5, 6].into_iter();
for (prev, next) in some_iter.tuples() {
println!("{}--{}", prev, next);
}
}
( aire de jeux )
1--2
3--4
5--6
Ainsi que jusqu'à 4 fenêtres Tuple avec Itertools::Tuple_windows
:
extern crate itertools;
use itertools::Itertools;
fn main() {
let some_iter = vec![1, 2, 3, 4, 5, 6].into_iter();
for (prev, next) in some_iter.Tuple_windows() {
println!("{}--{}", prev, next);
}
}
( aire de jeux )
1--2
2--3
3--4
4--5
5--6