In implementing this I found a few bits of friction, but eventually worked things out to meet my intended use case. So, while this does what I need (and I thought it was interesting), I do also wonder if there might be other, further FP / immutable state / Haskell-esque implementation options I'm still missing, as that is a paradigm in which I'm still trying to improve my 'thinking'. But, really, any thoughts are welcome!
The last loops is simply expressed as a fold()
.
By the way, all of the closures are unnecessary (just name the trait method!), and so is the last loop explicitly dropping items. When a vector/box/other collection is dropped, it frees its elements. Ranges are already iterators, so (0..3).into_iter()
is also unnecessary.
The simplified code (Playground):
fn main() {
let for_each = (0..3)
.map(|i| Box::new(A(i)))
.collect::<Vec<_>>();
println!("Initial state:\n {:?}", for_each);
let for_each = for_each.into_iter()
.map(<_>::into_next)
.collect::<Vec<_>>();
println!("\nIterated once, now state is:\n {:?}", for_each);
let for_each = (0..10).fold(for_each, |state, _i| {
state.into_iter()
.map(<_>::into_next)
.collect()
});
println!("\nIterated in a loop, now state is:\n {:?}", for_each);
}
Thanks, those are some good notes. Getting rid of the closures is particularly nice. And now I see that's how the drop
line was already working (it was just in there to clear the compiler warning, before I had it print to actually used the values).
So, now I've got a question on the fold as loop replacement. I've switched to fold_while()
from itertools so I could use my own exit conditions, but it still feels a bit rough. Is this the cleanest I'll be able to make this [playground]?
let x: f64 = 0.0;
// fold_while does what I want, but requires matching on exit to parse:
let for_each = match (0..)
.fold_while((for_each, x), |(state, x), _i| {
if x >= 10.0 {
FoldWhile::Done((state, x))
} else {
let x = x + 0.1;
FoldWhile::Continue((state.into_iter()
.map(BoxedDynInto::into_next)
.collect::<Vec<_>>(), x))
}
}) {
FoldWhile::Done((state, _x)) => state,
_ => panic!(),
};
Why don't you just use the into_inner()
method?
Apart from not matching, you could clean the code up syntactically by removing/shuffling around some type annotations and by introducing some additional bindings. Playground:
fn main() {
let for_each: Vec<_> = (0..3).map(|i| Box::new(A(i))).collect();
println!("Initial state:\n {:?}", for_each);
let for_each: Vec<_> = for_each.into_iter().map(<_>::into_next).collect();
println!("\nIterated once, now state is:\n {:?}", for_each);
let x: f64 = 0.0;
let (for_each, _) = (0..).fold_while((for_each, x), |(state, x), _i| {
if x >= 10.0 {
FoldWhile::Done((state, x))
} else {
let state = state.into_iter().map(<_>::into_next).collect();
let x = x + 0.1;
FoldWhile::Continue((state, x))
}
}).into_inner();
println!("\nIterated in a loop, now state is:\n {:?}", for_each);
}
Ah, that's what I was missing. Thanks again! I was getting stuck trying:
let FoldWhile::Done((for_each, _)) = (0..).fold_while(/* do stuff */)
Apparently the answer was to just forge ahead and find that rayon already has collect_into_vec
as a recommended option, unless I'm misunderstanding their claim that it "reuses the same backing buffer."
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.