Rust: Idomatic way to iterate over columns of a 2d vector - multidimensional-array

I am new to Rust and wanted to know whats the idomatic way to iterate over columns on a 2d vector.
I know how to do it for a rows, for example finding maximum for each row as below
let my_2d_vector: Vec<Vec<u64>> = vec![];
let max_in_each_rows = my_2d_vector.iter()
.map(|&row| row.iter().max())
.collect::<Vec<64>>();
How do I find maximum for each column without using loops?

One way to approach this functionally would be something like this:
let my_2d_vector: Vec<Vec<u64>> = vec![vec![1, 4, 3], vec![3, 3, 3]];
let max_in_each_col =
my_2d_vector
.iter()
.skip(1)
.fold(my_2d_vector[0].clone(), |mut maxes, row| {
maxes
.iter_mut()
.zip(row.iter())
.for_each(|(m, col)| *m = std::cmp::max(*m, *col));
maxes
});
println!("{:?}", max_in_each_col); // [3, 4, 3]
However, it's a misconception that loops aren't idiomatic Rust. You may find that the above isn't all that readable, and rustfmt formats it into 11 lines. Whereas using loops, although the logic is exactly the same, you get this
let my_2d_vector: Vec<Vec<u64>> = vec![vec![1, 4, 3], vec![3, 3, 3]];
let mut max_in_each_col = my_2d_vector[0].clone();
for row in &my_2d_vector[1..] {
for (m, col) in max_in_each_col.iter_mut().zip(row.iter()) {
*m = std::cmp::max(*m, *col);
}
}
println!("{:?}", max_in_each_col); // [3, 4, 3]
in only 6 lines and very readably.

Related

ndarray: Is there an effective way to swap 2 row/cols/etc. in different arrays?

Using ndarray with 2 Array structs, is there an effective way to swap 2 rows/cols/(slices along some axis)?
The title really sums up the question.
While different people might mean different things by "effective", a straightforward way is to assign the rows/columns to each other by means of a temporary. Here is an example for two rows (Playground):
let mut a = arr2(&[[1, 2, 3], [4, 5, 6]]);
let mut b = arr2(&[[7, 8, 9], [10, 11, 12], [13, 14, 15]]);
let mut a_row = a.slice_mut(s![1, ..]);
let mut b_row = b.slice_mut(s![2, ..]);
let tmp = a_row.to_owned();
a_row.assign(&b_row);
b_row.assign(&tmp);
println!("a = {:?}", a);
println!("b = {:?}", b);
Alternatively, you can use azip!() to swap elementwise and avoid creating a temporary array, or even par_azip! to parallelize the copying.

Rust: "multiple applicable items in scope" error for ndarray dot product on slice

I think I ended up figuring out a solution while asking this but figured I'd leave the question up anyway...
I'm working with n-dimensional matrices using the ndarray crate and I need to take dot-products of multiple non-contiguous slices. However, I run into an issue as the compiler can't seem to figure out if my slices are 1D or 2D when it comes to taking a dot product and I'm not sure how to specify this information.
Here is a simplified example:
use ndarray::prelude::*;
fn main() {
let a = array![[1, 2, 3], [1, 2, 3]];
println!("{:?}", &a);
let b = array![[1, 2, 3], [1, 2, 3]];
println!("{:?}", &b);
let a_slice = a.slice(s![0, ..]);
let b_slice = b.slice(s![0, ..]);
println!("{:?}", &a_slice.dot(&b_slice));
}
The error message is a bit confusing, and I don't know how to make the dot product use the right candidate given the abstracted structs involved:
error[E0034]: multiple applicable items in scope
--> src/main.rs:14:31
|
14 | println!("{:?}", &a_slice.dot(&b_slice));
| ^^^ multiple `dot` found
|
= note: candidate #1 is defined in an impl for the type `ndarray::ArrayBase<_, ndarray::dimension::dim::Dim<[usize; 1]>>`
= note: candidate #2 is defined in an impl for the type `ndarray::ArrayBase<_, ndarray::dimension::dim::Dim<[usize; 2]>>`
How do I provide the necessary information?
Despite the error message referring to ArrayBase, the important piece of information here is actually the dimensions of the slice. So providing explicit dimensions using ArrayView does the trick:
use ndarray::prelude::*;
fn main() {
let a = array![[1, 2, 3], [1, 2, 3]];
println!("{:?}", &a);
let b = array![[1, 2, 3], [1, 2, 3]];
println!("{:?}", &b);
let a_slice: ArrayView<_, Ix1> = a.slice(s![0, ..]);
let b_slice: ArrayView<_, Ix1> = b.slice(s![0, ..]);
println!("{:?}", &a_slice.dot(&b_slice));
}

How do I add the same integer to each element of a vector in Rust?

In Python, if I have a list and want to add 10 to each element I would do:
bar = [2,4,5,6,7]
bar = [x + 10 for x in bar]
resulting in: [12,14,15,16,17]. How can this be done in Rust? Is the only way doing a for loop and going through each vector element?
The Rust way to do this is very similar to Python: use iterators! The rough equivalent to Python's list comprehension is iter::map to get the new elements, and iter::collect to collect into a new vector (or some other kind of collection).
So for example, if bar is a Vec<i32> (or any other primitive integer type) and you want to add 10 to each element, try
bar = bar.into_iter().map(|x| x + 10).collect();
(playground)
Alternatively, you could mutate the elements in-place with
bar.iter_mut().for_each(|x| *x += 10);
(playground)
This is basically like a for loop, but a bit more succinct. This is generally going to be more efficient than the first method since you don't need to allocate a new vector (a sufficiently smart compiler may be able to avoid this). The only downside is that this version is less flexible. The output still needs to be a vector; you couldn't switch to a hash set or what have you. You also wouldn't be able to keep a copy of the old vector. See below for some examples of what's possible.
fn main() {
let mut bar = vec![2, 4, 5, 6, 7];
// Overwrite the old vector
bar = bar.into_iter().map(|x| x + 10).collect();
println!("new bar: {:?}", bar);
let bar = vec![2, 4, 5, 6, 7];
// Make a completely new vector
// Note that this works only because i32 implements the Copy trait,
// so we can make copies of the elements of bar without any problems
// In more general situations, we may need to clone each element
let foo: Vec<_> = bar.iter().map(|&x| x + 10).collect();
println!("old bar: {:?} (it's still around)", bar);
println!("new foo: {:?}", foo);
use std::collections::HashSet;
let bar = vec![2, 4, 5, 6, 7];
// transform the data and collect it into a HashSet
// instead of a vector
let bar: HashSet<_> = bar.into_iter().map(|x| x + 10).collect();
println!("new bar: {:?} (note that now bar is unordered)", bar);
let mut bar = vec![2, 4, 5, 6, 7];
// Overwrite the old vector in place
bar.iter_mut().for_each(|x| *x += 10);
println!("new bar: {:?}", bar);
}
(playground)
This is basic code which illustrates how to do it the way the question assumes by default. It might be useful for beginners with Rust like me:
fn increment_mut(p: &mut Vec<i32>, to_add: i32){
for i in 0..p.len() {
p[i] += to_add;
}
}
fn main() {
let mut p = vec![2, 4, 5, 6, 7];
increment_mut(&mut p, 10);
// Print the complete vector in Debug.
println!("{:?}", p)
}
$ cargo run
[12, 14, 15, 16, 17]
Using iter_mut
fn increment_mut2(p: &mut Vec<i32>, to_add: i32) {
for x in p.iter_mut() {
*x += to_add;
}
}
fn main() {
let mut p = vec![2, 4, 5, 6, 7];
increment_mut2(&mut p, 10);
// Print the complete vector in Debug.
println!("{:?}", p)
}
$ cargo run
[12, 14, 15, 16, 17]

How should I store a list of items from which I will be removing, but never adding?

I'm implementing a function where I'll be repeatedly eliminating values from a large list, and passing a copy of this list as a vector into another function each iteration:
let mut v = vec![5, 4, 4, 2, 6, 5, 1, 8, 2, 1, 6, 5, 4, 2, 0, 1];
for i in 0..10 {
println!("{}", Vector::from(v).iter().sum());
v.retain(|x| x > i);
}
If v is very large, this will be slow. Is there a better way? I tried:
let mut v = vec![5, 4, 4, 2, 6, 5, 1, 8, 2, 1, 6, 5, 4, 2, 0, 1];
let mut v = v.into_iter().map(|x| Some(x)).collect();
(and then replace the "deleted" values with None) but this just seemed unwieldy to convert to and from an ordinary Vec.
How should I be storing this list of values?
You can restructure your creation of the copied list to do the removal before the copy:
for i in 0..10 {
let dup = your_list.iter().filter(|n| n > i).collect::<Vec<_>>();
use_it(dup);
}
If it is important to your use case that you are left with a filtered Vec, and cannot change the collection type, then this is probably the most useful means. If the filters are cumulative, you can overwrite the original Vec with the filtered Vec each iteration to reduce the workload for each future iteration.
let mut list = your_list;
for i in 0..10 {
list = list.iter().filter(|n| n > i).collect();
use_it(list.clone());
}
The question you asked is directly answered by reshaping how you filter and duplicate the vector, but if you are able to change your types, then the answers below may be more worthwhile.
If your use_it function does not require a Vec or slice, then you may be better served by restructuring the consumer to take an iterator of numbers, and passing in your_list.iter().filter(...). This will result in no copying or rearranging in memory, and the consumer function will just skip the invalid values.
If you care more about counting how many times numbers appear in a collection, and do not specifically need a sequential list in memory, you can rearrange your list into a HashMap:
use std::collections::HashMap;
let mut dict: HashMap<i32, usize> = HashMap::new();
for num in your_list {
*dict.entry(num).or_insert(0) += 1;
}
and then you can filter numbers out of the map with constant-time access rather than linear-time in the size of the collection.
Since this is a question about performance, then you will need to benchmark everything so that you can test your assumptions. That being said and unless there's something smart to do inside the function you call (maybe only copying lazily the items you want to mutate), then I think your retain+clone approach is close to the fastest you can do. Using Options is almost certainly a bad idea: it adds checks everywhere and it kills cache locality.
The only thing that may improve performance is to do the copy and filtering in a single pass:
let mut v = vec![5, 4, 4, 2, 6, 5, 1, 8, 2, 1, 6, 5, 4, 2, 0, 1];
let mut work = v.clone();
for i in 0..10 {
println!("{}", work.iter().sum::<i32>());
work.clear();
v.retain(|&x| if (x > i) { work.push (x); true } else { false });
}
playground
Note that this will probably not make any difference if your data fits in the cache. In any case, benchmark, benchmark, benchmark! Lots of assumptions get proven wrong in the face of compiler optimizations and modern CPU architecture.
If you're removing the elements in order you should consider a queue. Using remove() takes O(1) time to remove an element, because it is essentially a dequeue or a pop.

JavaFX: concatenating sequences

Is there a standard library function or built-in construct to concatenate two sequences in JavaFX?
Here a Sequences.concatenate() function is mentioned, but it is nowhere to be seen in the official API.
Of course one could iterate over each sequence, inserting the values into a new sequence e.g:
function concatenate(seqA: Object[], seqB: Object[]) : Object[] {
for(b in seqB) insert b into seqA;
seqA;
}
..but surely something as basic as concatenation is already defined for us somewhere..
It is very simple, since there cannot be sequence in sequence (it all gets flattened), you can do it like this:
var a = [1, 2];
var b = [3, 4];
// just insert one into another
insert b into a;
// a == [1, 2, 3, 4];
// or create a new seq
a = [b, a];
// a == [3, 4, 1, 2];
Hope that helps.

Resources