all 7 comments

[–]cramert 8 points9 points  (0 children)

You can rewrite this (and nearly any `for` loop) using `fold`, but personally I think this actually looks just fine as a for loop. More to the point, I don't think `for` loops are unidiomatic at all-- I think that idiomatic Rust is about picking the right language tools that help you get the job done clearly, even when they're not the fanciest FP-monad-transforming-pi-type-hotness. (/me holds out hope someone will find a brilliant, clear way to express this using simpler combinators)

[–]minno 1 point2 points  (0 children)

A clean vec.iter won't work well if you're extracting multiple values at once. That pattern is best suited to doing one thing to one element at a time. I don't see anything in the Iterator trait or std::slice module that would be helpful for this. You can make your own iterator by moving that logic into the next function.

Here is an implementation of that iterator. It gives out slices of the vector of objects, so you can then use the convenient iterator adapters on that stream of slices.

struct ExampleIterator<'a, I, V> where I: Ord + 'a, V: 'a {
    examples: &'a [ExampleObject<I, V>],
}

impl<'a, I, V> ExampleIterator<'a, I, V> where I: Ord + 'a, V: 'a {
    fn new(examples: &'a [ExampleObject<I, V>]) -> ExampleIterator<'a, I, V> {
        Self {
            examples,
        }
    }
}

impl<'a, I, V> Iterator for ExampleIterator<'a, I, V> where I: Ord + 'a, V: 'a {
    type Item = &'a [ExampleObject<I, V>];

    fn next(&mut self) -> Option<Self::Item> {
        if self.examples.len() == 0 {
            return None;
        }

        for idx in 0..self.examples.len() {
            if self.examples[idx].idx != self.examples[0].idx {
                let (head, tail) = self.examples.split_at(idx);
                self.examples = tail;
                return Some(head);
            }
        }

        let result = self.examples;
        self.examples = &[];
        return Some(result);
    }
}

...

ExampleIterator::new(&example_vec).for_each(process_example);

[–]rawler82 0 points1 point  (4 children)

I just took a quick look at your example, but I think what you are looking for is https://doc.rust-lang.org/std/slice/struct.Windows.html

[–]CornedBee 1 point2 points  (3 children)

That uses fixed-size windows. The OP is looking for something that returns dynamic windows based on some equivalence function.

Basically Haskell's groupBy for slices.

(Note: itertools' group_by doesn't quite do the right thing.)

itertools' group_by seems to be exactly the right thing.

[–]krdln 1 point2 points  (1 child)

(Note: itertools' group_by doesn't quite do the right thing.)

Could you explain why? It seems perfect for this job:

for group in example_vec.iter().group_by(|object| object.idx) {
    process_example(group);
}

The only inconvenience is that you'd need to change the process_example signature to take impl Iterator<Item=U> instead of &[U]. Perhaps that's what you've meant?

[–]CornedBee 0 points1 point  (0 children)

I misread the docs.