Last active
September 17, 2018 12:01
-
-
Save tuzz/0a303816865b5ef5a8365c35568a9cb5 to your computer and use it in GitHub Desktop.
An attempt to write a function 'zip_if' in Rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Implements: https://gist.github.com/jcoglan/adc4ec173017e48f5ce68d6568fc5860 | |
// | |
// The basic idea is we store a vector of cursors that advance through the | |
// nested list and build up the result. If the predicate for the current | |
// element is false we append a row padded with zeroes, otherwise we move | |
// along to the next nested list until we reach the last one. When we do, | |
// we append the fully saturated row to the result and move to the next | |
// element in each of the nested lists. | |
// | |
// At the end we need to check if there are any elements that matched the | |
// predicate that still need to be 'flushed' to the result. We do this by | |
// keeping track of a boolean that is true whenever the predicate matches and | |
// set to false whenever we append a fully saturated row to the result. If | |
// the terminating condition is reached and this boolean is still true then | |
// we look up the elements that haven't run off the end of the cursors and | |
// append it to result. | |
// | |
// This isn't a particularly nice Rust implementation and it has some | |
// duplication but it was the first idea I thought of. It's also limited to | |
// only work on types that implement Copy because it was easier than having | |
// to clone in a couple of places and deal with ownership. | |
fn zip_if<T, P>(list: Vec<Vec<T>>, predicate: P, placeholder: T) -> Vec<Vec<T>> | |
where T: Copy + std::fmt::Debug, | |
P: Fn(T) -> bool | |
{ | |
let mut result = vec![]; | |
let mut cursors = vec![0; list.len()]; | |
let mut index = 0; | |
let mut flush = false; | |
loop { | |
let nested = match list.get(index) { | |
Some(n) => n, | |
None => { | |
if flush { | |
let row = cursors.iter().enumerate() | |
.map(|(i, &c)| *list[i].get(c).unwrap_or(&placeholder)).collect(); | |
result.push(row); | |
} | |
break; | |
}, | |
}; | |
let cursor = cursors[index]; | |
let element = *match nested.get(cursor) { | |
Some(e) => e, | |
None => { index += 1; continue }, | |
}; | |
if predicate(element) { | |
if index < list.len() - 1 { | |
index += 1; | |
flush = true; | |
continue; | |
} | |
let row = cursors.iter().enumerate().map(|(i, &c)| list[i][c]).collect(); | |
result.push(row); | |
cursors = cursors.iter().map(|c| c + 1).collect(); | |
index = 0; | |
flush = false; | |
} else { | |
let row = (0..list.len()) | |
.map(|i| if i == index { element } else { placeholder }).collect(); | |
result.push(row); | |
cursors[index] += 1; | |
} | |
} | |
result | |
} | |
#[test] | |
fn it_works_for_the_given_example() { | |
let example = vec![ | |
vec![28, 74, 59, 54, 95], | |
vec![67, 22, 44, 23, 79], | |
vec![52, 11, 75, 10, 34], | |
]; | |
let result = zip_if(example, |x| x % 2 == 1, 0); | |
assert_eq!(result, vec![ | |
vec![28, 0, 0], | |
vec![74, 0, 0], | |
vec![ 0, 0, 52], | |
vec![59, 67, 11], | |
vec![54, 0, 0], | |
vec![ 0, 22, 0], | |
vec![ 0, 44, 0], | |
vec![95, 23, 75], | |
vec![ 0, 0, 10], | |
vec![ 0, 0, 34], | |
vec![ 0, 79, 0], | |
]); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment