#rust #iterator #delimiter #yield
#Ржавчина #итератор #разделитель #выход
Вопрос:
Я хотел бы избежать экспериментальных функций, поэтому я рассматриваю другие варианты перед генераторами.
У меня есть вектор v
, который разделен на группы 0. Сумма этих групп с разделителями может быть накоплена с помощью цикла for или итератора:
fn main() {
let v = vec![55, 23, 75, 0, 12, 34, 0, 97, 71, 23, 0];
// for loop
let mut acc = 0;
for amp;vi in amp;v {
acc = vi;
if vi == 0 {
println!("for yield {}", acc);
acc = 0;
}
}
// iter 'loop'
v.iter()
.scan(0, |acc, amp;vi| {
*acc = vi;
Some(if vi == 0 {
let total = *acc;
*acc = 0;
Some(total)
} else {
None
})
})
.filter_map(|acc| acc)
.for_each(|acc| println!("iter yield {:?}", acc));
}
scan
используется как сопрограмма ad hoc, которая возвращает, Some(value)
когда итератор выдал значение и None
когда итератор все еще обрабатывает. None
отфильтровывается и выводятся суммы.
Приведенный выше пример несколько тривиален, поскольку обе операции приводят к одному и тому же результату; однако мой проект требует переваривания потока данных (не могут быть собраны в вектор) и их условного сворачивания (например, условием здесь может быть, если acc
делится на 10, а не разделяется 0).
Само условие сворачивания конвейерное, так что каждый конвейер может предоставлять итератор (подумайте о вложенных сопрограммах). Я хотел бы посмотреть, есть ли альтернатива Iterator::scan
-> Iterator::filter_map
, используют ли они итераторы или нет. Имейте в виду, что сбор всего потока данных невозможен, поскольку поток потенциально неограничен.
Комментарии:
1. разделение на группы по нулям должно быть максимально простым,
v.split(|vi| vi == 0)
и затем вы можете использовать.map(|g| g.sum())
или что-то подобное. Однако у меня нет лучшего решения для разделения на основе самой суммы.2. @kmdreko Есть ли способ сделать это с помощью итераторов? Я сжимаю большой поток объектов, а само сжатие конвейерно разделено на 4 этапа, поэтому сбор в вектор приведет к нежелательной задержке. Я вижу, что черта разделения специализирована для строковых типов. Интересно, смогу ли я избежать ручной реализации этого для векторов.
3. Эта дополнительная информация о том, как вы ее используете, является ключом к тому, чтобы сделать вопрос несубъективным. В настоящее время у вас есть только два разных способа сделать одно и то же, оба из которых работают, и неопределенный запрос сделать его «более идиоматичным». Если код, приведенный в вопросе, не соответствует вашим потребностям, важно указать в вопросе, почему он не соответствует вашим потребностям, и объяснить, какой ответ был бы приемлемым.
4. @trentcl Я добавлю дополнительную информацию в вопрос. Спасибо вам за предложение.
5. Меня смущает ваше использование термина «сопрограмма» применительно к
scan
. Какscan
используется здесь как сопрограмма? Сопрограмма должна либо иметь возможность приостановить выполнение, либо переключить его на кого-то другого, а ваш код не делает ни того, ни другого, это обычная подпрограмма. Название также относится к сопрограммам без объяснения связи.
Ответ №1:
Нет ничего плохого ни в for
цикле, ни в версии с scan()
. Если бы я искал более понятную альтернативу, я бы подумал об использовании group_by
из itertools
ящика. Эта версия элегантна и делает почти то, что вам нужно:
use itertools::Itertools;
v.iter()
.group_by(|amp;amp;vi| vi != 0)
.into_iter()
.map(|(_, group)| group.sum::<u32>())
.for_each(|s| println!("sum {}", s));
Проблема в том, что он выводит числа 153, 0, 46, 0, 191, и 0. Значения 0 исходят из того факта, что он рассматривает элемент 0 как просто еще одну группу, для которой ключ (результат выражения vi != 0
) оказывается ложным, а не истинным. Чтобы исправить это, вам нужно изменить map
на filter_map
аналогично тому, как вы сделали с scan()
:
v.iter()
.group_by(|amp;amp;vi| vi != 0)
.into_iter()
.filter_map(|(filt, group)| if filt { Some(group.sum::<u32>()) } else { None })
.for_each(|s| println!("sum {}", s));
Не идеальный, но все же немного более элегантный, чем оригинальная формулировка, потому что он делает группировку явной.
Комментарии:
1. Мой вопрос был неправильно сформулирован. Примите мой голос, но я закрою этот вопрос и опубликую исправленную версию.