rust - ps4 - ¿Cómo puedo iterar sobre un rango con un paso personalizado?
rust traduccion (5)
¿Cómo puedo iterar sobre un rango en Rust con un paso que no sea 1? Vengo de un fondo de C ++, así que me gustaría hacer algo como
for(auto i = 0; i <= n; i+=2) {
//...
}
En Rust necesito usar la función de range
, y no parece que haya un tercer argumento disponible para tener un paso personalizado. ¿Cómo puedo lograr esto?
Me parece que hasta que el método .step_by
se estabilice, uno puede lograr fácilmente lo que quiere con un Iterator
(que es lo que realmente son Range
s de todos modos):
struct SimpleStepRange(isize, isize, isize); // start, end, and step
impl Iterator for SimpleStepRange {
type Item = isize;
#[inline]
fn next(&mut self) -> Option<isize> {
if self.0 < self.1 {
let v = self.0;
self.0 = v + self.2;
Some(v)
} else {
None
}
}
}
fn main() {
for i in SimpleStepRange(0, 10, 2) {
println!("{}", i);
}
}
Si uno necesita iterar múltiples rangos de diferentes tipos, el código puede hacerse genérico de la siguiente manera:
use std::ops::Add;
struct StepRange<T>(T, T, T)
where for<''a> &''a T: Add<&''a T, Output = T>,
T: PartialOrd,
T: Clone;
impl<T> Iterator for StepRange<T>
where for<''a> &''a T: Add<&''a T, Output = T>,
T: PartialOrd,
T: Clone
{
type Item = T;
#[inline]
fn next(&mut self) -> Option<T> {
if self.0 < self.1 {
let v = self.0.clone();
self.0 = &v + &self.2;
Some(v)
} else {
None
}
}
}
fn main() {
for i in StepRange(0u64, 10u64, 2u64) {
println!("{}", i);
}
}
Dejaré que elimines la verificación de límites superiores para crear una estructura abierta si se requiere un ciclo infinito ...
Las ventajas de este enfoque es que se trabaja con azúcar y continuará funcionando incluso cuando las características inestables sean utilizables; además, a diferencia del enfoque desazufrado que utiliza el Range
estándar s, no pierde eficacia mediante múltiples llamadas .next()
. Las desventajas son que se necesitan unas pocas líneas de código para configurar el iterador, por lo que solo vale la pena para el código que tiene muchos bucles.
Si está pasando por algo predefinido y pequeño como 2, puede usar el iterador para realizar el paso manualmente. p.ej:
let mut iter = 1..10;
loop {
match iter.next() {
Some(x) => {
println!("{}", x);
},
None => break,
}
iter.next();
}
Incluso podría usar esto para avanzar en una cantidad arbitraria (aunque esto definitivamente se hace más largo y más difícil de digerir):
let mut iter = 1..10;
let step = 4;
loop {
match iter.next() {
Some(x) => {
println!("{}", x);
},
None => break,
}
for _ in 0..step-1 {
iter.next();
}
}
Usa la caja num con range_step
Usted escribiría su código C ++:
for (auto i = 0; i <= n; i += 2) {
//...
}
... en Rust como tal:
let mut i = 0;
while i <= n {
// ...
i += 2;
}
Creo que la versión de Rust es más legible también.
range_step_inclusive
y range_step
han desaparecido.
A partir de Rust 1.28, Iterator::step_by
es estable:
fn main() {
for x in (1..10).step_by(2) {
println!("{}", x);
}
}