I have to basically go through a picture and apply different filters to it. The basic code I use for that looks like this and runs for my example in ~0.37 seconds:
let mut changed = true;
while changed {
changed = false;
// I need to clone here so I dont use the changed data
let past = self.grid.clone();
// My data is in a vector, this gives me all x/y combinations
for (x, y) in GridIndexIter::from(0, self.width as i32, 0, self.height as i32) {
// This gives me the index of the current piece
let i = self.ind(x, y);
let root = past[i];
// Here I apply my filter (if no value, set it to a neighboring value)
if root.elevation == None {
let surround: Option<Tile> = GridIndexIter::from(-1, 2, -1, 2)
.map(|(x_add, y_add)| past[self.ind(x x_add, y y_add)])
.filter(|tile| tile.elevation != None)
.nth(0);
if let Some(tile) = surround {
self.grid[i].elevation = tile.elevation;
changed = true;
}
}
}
}
But because I want to run multiple filters, all of which are applied the same way but differ in the actual calculation (I might want to smooth the values and so on), I tried to split it into the basic logic of applying a filter and the logic of that filter, which I would set as a closure, but now it takes ~4,5 seconds for my example:
fn apply_filter(&mut self, condition: fn(Tile) -> bool, filter: fn(Vec<Tile>) -> Option<Tile>) {
let mut changed = true;
while changed {
changed = false;
let past = self.grid.clone();
for (x, y) in GridIndexIter::from(0, self.width as i32, 0, self.height as i32) {
let i = self.ind(x, y);
let root = past[i];
if condition(root) {
if let Some(tile) = filter(GridIndexIter::from(-1, 2, -1, 2)
.map(|(x_add, y_add)| past[self.ind(x x_add, y y_add)])
.collect()) {
self.grid[i].elevation = tile.elevation;
changed = true;
}
}
}
}
}
self.apply_filter(|tile| tile.elevation == None,
|past| {
if let Some(tile) = past.iter().filter(|tile| tile.elevation != None).nth(0) {
return Some(Tile {
elevation: tile.elevation,
..past[4]
});
} None
}
);
Did I make a mistake? Is there some way to make closure more efficient? Is there another way to achieve the same?
CodePudding user response:
Since it's very hard to reproduce your code, I just wanted to suggest a version that is a bit more clear, and maybe easier to debug. Obviously this is all untested, probably doesn't compile
loop {
let past = self.grid.clone();
let updates_count = GridIndexIter::from(0, self.width as i32, 0, self.height as i32)
.enumerate()
.filter(|(i,_)| &past[i].elevation.is_none()) // captures &past
.filter_map(|(i,(x,y))|
GridIndexIter::from(-1, 2, -1, 2) // captures &self, &past
.map(|(x_add, y_add)| &past[self.ind(x x_add, y y_add)])
.filter(|tile| tile.elevation.is_some())
.nth(0)
.map(|tile| (i,tile))) // either Some((i,tile)) or None
.map(|(i,tile)| self.grid[i].elevation = tile.elevation)
.count();
if updates_count == 0 {
break;
}
}
The version with closures could look like that (notice how filter
is defined, it accepts iterators, not vectors)
fn apply_filter(&mut self, condition: fn(Tile) -> bool, filter: fn(impl Iterator<Item=Tile>) -> Option<Tile>) {
loop {
let past = self.grid.clone();
let updates_count = GridIndexIter::from(0, self.width as i32, 0, self.height as i32)
.enumerate()
.filter(|(i,_)| condition(&past[i])) // captures &past
.filter_map(|(i,(x,y))|
filter(GridIndexIter::from(-1, 2, -1, 2) // captures &self, &past
.map(|(x_add, y_add)| &past[self.ind(x x_add, y y_add)]))
.map(|tile| (i,tile))) // either Some((i,tile)) or None
.map(|(i,tile)| self.grid[i].elevation = tile.elevation)
.count();
if updates_count == 0 {
break;
}
}
}
-- EDIT: The following, is a simplified version of how would an iterator could be passed to a capture: Playground of the following code
use std::ops::Fn;
#[derive(PartialEq, Debug)]
struct Tile(i32);
type TilesIter<'a> = &'a mut dyn Iterator<Item=Tile>;
fn apply_function<'a>(func: &'a dyn Fn(TilesIter) -> Option<Tile>) -> Option<Tile> {
let tiles = vec![Tile(0), Tile(1), Tile(2), Tile(3)];
func(&mut tiles.into_iter())
}
fn main() {
let result_tile = apply_function(&|iter: TilesIter| iter.last());
println!("{:?}", result_tile); // Some(Tile(3))
assert_eq!(result_tile, Some(Tile(3)));
}