Skip to content

Instantly share code, notes, and snippets.

@tuzz
Last active September 17, 2018 12:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tuzz/0a303816865b5ef5a8365c35568a9cb5 to your computer and use it in GitHub Desktop.
Save tuzz/0a303816865b5ef5a8365c35568a9cb5 to your computer and use it in GitHub Desktop.
An attempt to write a function 'zip_if' in Rust
// 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