Я хочу создать итератор, который выдает четные числа из значения в обратном порядке.
Эта программа демонстрирует проблему. Он считывает два целых числа из командной строки и печатает четные числа между ними в порядке убывания. Достаточно ожидать, что минимальное значение будет четным.
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}");
}
Однако, например, при вводе 2 6 программа выдает 5 3, хотя я ожидал, что числа будут 4 2.
Как создать итератор, который будет выдавать 4 2, поскольку необратимая версия выдает 2 4?
Что это &= !1 вообще делает? Злой маленький хак.
Но да, пожалуйста, ответьте, если считаете, что это будет оптимальный способ добиться этого.
Это маскирует младший бит и устанавливает его в ноль.
или переместите .rev() после .step_by(2)
@valllentin это вызовет ошибку компилятора.
Вы указываете "от любого максимума". Если вы хотите, чтобы код работал с любого минимума, вам также необходимо настроить минимум на следующее четное число, используя min += min & 1.
Какой «правильный итератор» вам нужен? Тот, который выводит 4 2, или тот, который выводит 6 4 2? Вам необходимо учитывать тот факт, что x.rev() — это не просто замена конечных точек и обратный отсчет.
То есть с помощью x вы всегда включаете x.start, но никогда не включаете x.end. С помощью x.rev() вы можете включить x.end и включать x.start только тогда, когда вы включаете x.end.
@chepner Я прояснил вопрос в этом аспекте.
@vahvero замена rev и step_by не приведет к ошибке компилятора вы, должно быть, напутали где-то еще.
step_by(2) — это хак, который выбирает четные числа только тогда, когда четные числа изначально находятся на четных позициях. Это верно для диапазонов (even..odd), но не для диапазонов в целом, и rev() не сохраняет свойство четных чисел в четных позициях. Проще всего было бы использовать filter для прямого выбора четных значений, независимо от того, где они находятся. (1/2)
Я предполагаю, что вы ищете что-то более эффективное, которое не требует применения предиката к каждому значению, а использует тот факт, что независимо от того, начинается ли итератор с четного или нечетного числа, четные и нечетные числа чередуются. Для этого вам необходимо настроить логику в зависимости от того, четное или нечетное первое число. (2/2)
@vahvero, вы получаете отрицательные голоса, потому что вопрос основан на ошибочной предпосылке; итератор и rev работают, как указано. Диапазон (min..max) оценивается как [min,max) и оценивается до вызова rev. Ваше ожидание того, что ваш код правильный, нереально.
@cafce25 выдает ошибку, если максимальное значение неизвестно во время компиляции. Я постарался сделать вопрос максимально простым, но если конечная точка является динамической, должна возникнуть ошибка «ошибка [E0277]: привязка признака std::ops::Range<i64>: ExactSizeIterator не удовлетворена».
@vahvero, похоже, ваш вопрос не включает минимально воспроизводимый пример тогда.
@cafce25 cafce25 Я изменил код, включенный в вопрос, чтобы лучше продемонстрировать вопрос.
Перестановка rev и step_by по-прежнему работает, если вы используете не i64 в качестве типа, а, например, i32 или isize.
@cafce25 Да, могу подтвердить. Я бы посчитал это лучшим ответом, чем принятый, без дополнительных звонков. Я даже не знал, что смена типа может повлиять на дальность.

step_by(2) работает в предположении, что итератор строго чередует четные и нечетные числа, но также начинается с четного числа. Это верно для Range, начинающегося с четного числа, но не верно для обращения каждого Range. В частности, первый элемент (2..6).rev() нечетный.
Чтобы разместить перевернутые диапазоны, которые могут начинаться с нечетных чисел, используйте 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() начинается с 5, а (2..6).rev().skips_while(...) начинается с 4. Обратите внимание, что (2..5).rev() и (2..5).rev().skips_while(...) будут эквивалентны, поскольку ничего из исходного итератора не пропускается.
Что касается эффективности, предикат будет применен не более чем к 2 элементам, чтобы удовлетворить предварительному условию, необходимому для step_by(2), по сравнению с
let a = (2..6).rev()
.filter(|x| x % 2 == 0)
.map(|x| x.to_string() + " ")
.collect();
который не может использовать какую-либо информацию о структуре (2..6).rev() для применения данного предиката менее чем O(n) раз.
Вы можете настроить максимум так, чтобы он был четным (
max &= !1), и использоватьmin..=maxв качестве диапазона.