#rust #iterator
#Ржавчина #итератор
Вопрос:
У меня есть Vec<bool>
, и я хочу создать итератор, который останавливается на первом и включает false
его; Как я могу это реализовать? Я рассматривал take_while
метод, но он останавливает один элемент раньше. Например, если входные данные были vec![true, true, true, false, false]
, мне нужен итератор, эквивалентный vec![true, true, true, false].iter()
, но я не могу найти хороший способ выразить это функциональным способом. Я рассмотрел следующие методы:
// method 1
// This one triggers the side effects of do_something_with_x
// for the right elements, but it seems like bad style. It
// also doesn't give me an iterator that I can chain on, I
// would need to use the side effect creatively.
input
.iter()
.map(|x| {
do_something_with_x(x);
x
})
.take_while(|x| *x)
.for_each(|_|());
// method 2
// This seems a little better, but I don't like how I have
// to iterate through it twice. My code isn't performance
// sensitive so that's not the reason, but it just looks bad to me.
let len = input.iter().position(|x| !*x);
let result = input.iter().take(len);
Я значительно упростил задачу, чтобы ее было легче понять, вектор другого типа, и у меня есть другой предикат, который я использую. Я также думаю о третьем методе, который включал бы архивирование двух итераторов одной и той же вещи, которые смещены на единицу, но я не совсем уверен, как это лучше реализовать, и, несмотря на это, он кажется не очень читаемым. Каков канонический способ решения такого рода проблем?
Ответ №1:
Вы можете использовать Iterator::scan
, который создает новый итератор, вызывая замыкание с каждым элементом и изменяемым состоянием.
Позволяя состоянию указывать, должны ли мы заканчиваться на следующем элементе, мы можем остановиться после первого ложного элемента:
input
.iter()
.scan(false, |is_done, item| {
if *is_done { return None; }
if !item { *is_done = true; }
Some(item)
})
.for_each(|_| {});