EN VI

Rust - Yielding even numbers in a reverse iterator?

2024-03-13 21:00:10
How to Rust - Yielding even numbers in a reverse iterator

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?

Solution:

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.

Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login