I wish to create an iterator which yields even numbers from a value in reverse order.
This program demonstrates the issue. It reads two integers from command line and prints even numbers between them in decreasing order. It is sufficient to expect min value to be even.
use std::io::{self, BufRead};
fn main() {
// Read numbers
let mut input = io::BufReader::new(io::stdin());
let mut buf = String::new();
input.read_line(&mut buf).unwrap();
let mut split = buf.split(" ");
let min = split.next().unwrap().trim().parse::<i64>().unwrap();
let max = split.next().unwrap().trim().parse::<i64>().unwrap();
// Attempt to generate the iterator
let payload: String = (min..max)
.rev()
.step_by(2)
.map(|x| x.to_string() + " ")
.collect();
println!("{payload}");
}
However, for example input of 2 6
, program yields 5 3
when I would expect the numbers to be 4 2
.
What is the way to create an iterator which would here produce 4 2
as unreversed version does yield 2 4
?
step_by(2)
works under the assumption of an iterator that strictly alternates even and odd numbers, but also starts with an even number. That's true for a Range
that starts with an even number, but not true for the reversal of every Range
. In particular, the first element of (2..6).rev()
is odd.
To accommodate reversed ranges that might start with odd numbers, use skip_while
:
let a = (2..6).rev()
.skip_while(|x| x % 2 == 1)
.step_by(2)
.map(|x| x.to_string() + " ")
.collect();
println!("{a}");
(2..6).rev()
starts with 5
, but (2..6).rev().skips_while(...)
starts with 4
. Note that (2..5).rev()
and (2..5).rev().skips_while(...)
would be equivalent, as nothing from the original iterator is skipped.
Regarding efficiency, the predicate will be applied to at most 2 elements in order to satisfy the precondition required for step_by(2)
, compared to
let a = (2..6).rev()
.filter(|x| x % 2 == 0)
.map(|x| x.to_string() + " ")
.collect();
which can't use any information about the structure of (2..6).rev()
to apply the given predicate fewer than O(n) times.