5

I would like to avoid experimental features so I am considering other options before generators.

I have a vector v which is delimited into groups by 0. The sum of these delimited groups can be accumulated through a for loop or an iterator:

fn main() { let v = vec![55, 23, 75, 0, 12, 34, 0, 97, 71, 23, 0]; // for loop let mut acc = 0; for &vi in &v { acc += vi; if vi == 0 { println!("for yield {}", acc); acc = 0; } } // iter 'loop' v.iter() .scan(0, |acc, &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 is used as an ad hoc coroutine which returns Some(value) when the iterator has produced a value and None when the iterator is still processing. None is filtered out and the sums are printed.

The example above is somewhat trivial as both operations produce the same result; however, my project requires digesting a stream of data (cannot be collected into vector) and conditionally folding them (e.g. a condition here could be if the acc is divisible by 10, instead of 0 delimited).

The folding condition itself is pipelined such that each pipeline may provide an iterator (think nested coroutines). I'd like to see if there is an alternative to Iterator::scan -> Iterator::filter_map whether they use iterators or not. Keep in mind, collecting the entire stream of data is not possible as the stream is potentially unlimited.

8
  • 1
    splitting into groups by zeroes should be as simple as v.split(|vi| vi == 0) and then you can use .map(|g| g.sum()) or similar. I don't of a better solution for splitting based on the sum itself though. Commented Sep 15, 2020 at 22:32
  • @kmdreko Is there a way to do this with iterators? I am compressing a large stream of objects, and the compression itself is pipelined into 4 stages so collecting into a vector would cause undesirable latency. I see that the Split trait is specialized to String types. I wonder if I can avoid manually implementing that for vectors. Commented Sep 15, 2020 at 22:37
  • That extra information about how you're using it is key to making the question non-subjective. Currently you just have two different ways of doing the same thing, both of which work, and a vague request to make it "more idiomatic". If the code you have in the question does not meet your needs, it's important to include in the question why it does not meet your needs, and explain what kind of answer would be acceptable. Commented Sep 15, 2020 at 23:16
  • @trentcl I will add the extra information into the question. Thank you for the suggestion. Commented Sep 15, 2020 at 23:20
  • 1
    I am confused by your use of the term "coroutine" in reference to scan. How is scan used like a coroutine here? A coroutine should either be able to suspend execution or switch it to someone else, and your code does neither, it's an ordinary subroutine. The title also refers to coroutines without explaining the connection. Commented Sep 16, 2020 at 7:00

1 Answer 1

4

There is nothing wrong with either the for loop or the version with scan(). If I were looking for a clearer alternative, I'd consider using group_by from the itertools crate. This version is elegant and does almost what you need:

use itertools::Itertools; v.iter() .group_by(|&&vi| vi != 0) .into_iter() .map(|(_, group)| group.sum::<u32>()) .for_each(|s| println!("sum {}", s)); 

The problem is that it outputs the numbers 153, 0, 46, 0, 191, and 0. The 0s come from the fact that it considers the element 0 as just another group for which the key (result of the expression vi != 0) happens to be false rather than true. To fix this, you need to change map to filter_map similar to how you did with scan():

v.iter() .group_by(|&&vi| vi != 0) .into_iter() .filter_map(|(filt, group)| if filt { Some(group.sum::<u32>()) } else { None }) .for_each(|s| println!("sum {}", s)); 

Not perfect, but still slightly more elegant than the original formulation because it makes the grouping explicit.

Sign up to request clarification or add additional context in comments.

1 Comment

My question was not correctly phrased. Take my upvote, but I will close out this question and repost the corrected version.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.