Is it possible to iterate through two arrays in a for loop? I tried the following code but I does not compile:
let a = [0, 1, 2, 3, 4];
let mut b = [0; 4];
for (ai, bi) in (a.iter(), b.iter_mut()) {
*bi = 2 * *ai;
}
Thanks
Is it possible to iterate through two arrays in a for loop? I tried the following code but I does not compile:
let a = [0, 1, 2, 3, 4];
let mut b = [0; 4];
for (ai, bi) in (a.iter(), b.iter_mut()) {
*bi = 2 * *ai;
}
Thanks
Thank you for suggesting that! (sorry I hadn't found it myself).
Now I have it working:
let a = [0, 1, 2, 3, 4];
let mut b = [0; 4];
for it in a.iter().zip(b.iter_mut()) {
let (ai, bi) = it;
*bi = 2 * *ai;
}
However, don't you think the notation above would have been more clear?
Thanks
Your initial notation would require special language support to recognize if I'm not mistaken. That has to be balanced against clarity and the cost of alternatives. In this case, zip
is a pretty standard solution to your problem. (Although perhaps it is more well known to the functional programmers.)
I wonder, if zip
should accept IntoIterator
instead of Iterator
.
What do you mean by "special language support"? Couldn't it be implemented with impl IntoIterator for (A, B) where A: Iterator, B: Iterator
?
Probably. My mistake. I shouldn't make such claims so early in the morning.
It has been discussed and postponed until more thoughts come up in this direction (and probably until variadic generics)
Closed RFC-PR:
https://github.com/rust-lang/rfcs/pull/870
Open RFC-Issue:
https://github.com/rust-lang/rfcs/issues/930
Internals-discussion:
FYI, you can use for (ai, bi) in
(and so on) here.
Thank you very much for directing me to those links, I'll add my big to the thread on internals.rust-lang.org.
In my opinion tuple notation would be very convenient and clear. The specific iteration sequence could be specified through brackets nesting.
For instance:
for (x, y, z) in (1..10, 2..11, 3..13) {...}
would give ((1,2,3), (2,3,4) ... (9,10,12))
for (x, y, z) in (1..10, (2..11, 3..13)) {...}
would give ((1,2,3), (1,3,4) ... (2,2,3), (2,3,4) ... (9,10,12))
for (x, y, z) in (1..10, (2..11, (3..13))) {...}
would give ((1,2,3), (1,2,4) ... (1,3,3), (1,3,4) ... (9,10,12))
This notation in my opinion would be much clearer and compact than using .zip()
.
I'll add here another point which is still related to iterating through two arrays. Things get more complex with multidimensional arrays. In this case instead of writing:
for row in m1.iter_mut() { for el in row.iter_mut() {...} }
it would be convenient to be able to write:
for el in m1.iter_mut().iter_mut() {...}
I'll clarify this point with the following example. I have two 2D arrays, and I need to cycle through each element of them, so I could write for instance:
for i in 0..s {
for j in 0..s {
m2[i][j] = m1[i][j].powi(2);
}
}
To avoid bound checking, I would prefer to use .iter()
, but this is what I need to write with the current Rust notation:
for (m1_row, m2_row) in m1.iter().zip(m2.iter_mut()) {
for (m1_el, m2_el) in m1_row.iter().zip(m2_row.iter_mut()) {
*m2_el = m1_el.powi(2);
}
}
This looks quite complex, it would be nice to be able to write:
for (m1_el, m2_el) in (m1.iter().iter(), m2.iter_mut().iter_mut()) {
*m2_el = m1_el.powi(2);
}
Here is another example:
for i in 0..s {
for j in 0..s {
if ((m2[i][j]-m1[i][j])/m1[i][j]).abs() > 1E-6 { test = false; }
}
}
In this case I could not figure out how to use .iter()
at all... (I tried different possibilities but I always get compilation errors one way or another).
Thanks
Edit: now the following code works, I guess something was corrected in the last nightly releases:
for (m1_row, m2_row) in m1.iter().zip(m2.iter()) {
for (m1_el, m2_el) in m1_row.iter().zip(m2_row.iter()) {
if ((m2_el-m1_el)/m1_el).abs() > 1E-6 { test = false; }
}
}
I'm not sure if that would be a very good general solution because you might want to iterate row first or column first. Theoretically, you could do something like this:
// joint_index would return an tuple of iterators which is based
// on the order of numbers. This case would be equivalent to
// `array1[k,i,j]`. `joint_index(0,1,2)` would be `array1[i,j,k]`.
for (a1, a2, a3) in (array1, array2, array3).joint_index(2, 0, 1) {
if ((a2-a1)/a1).abs() > 1E-6 { test = false; }
}
Having said that, I don't know how to make your example work.
To specify the iteration order, an optional integer parameter could be added to .iter()
, with default 0
indicating iteration over the first dimension available.
To iterate over two dimensions in order (first-dimension, then second-dimension), one could write either:
for el in a2D.iter(0).iter(0) {...}
or:
for el in a2D.iter().iter() {...}
while to iterate in reverse order (second-dimension, then first-dimension), one could write either:
for el in a2D.iter(1).iter(0) {...}
or:
for el in a2D.iter(1).iter() {...}
Considering a 3D array, to iterate in order over second-dimension, first-dimension, third-dimension, one could write either:
for el in a3D.iter(2).iter(0).iter(0) {...}
or:
for el in a3D.iter(2).iter().iter() {...}
I jus tried to use this but am told that iter does not take a parameter.
What @lucatrv was typing was just an idea.
To iterate several iterators at a time we use .zip()
:
for (a, b) in array1.iter().zip(array2.iter()) {
}
Hi, For processing verticies in 3D or audio buffers, the following process is commonly used in C
void add(float* arr1, float* arr2, float* res) {
while(arr1){
(*res)++ = (*arr1)++ + (*arr2)++; (ugly to read but reveals what is really being performed)
}
}
which mean we iterate as long as the pointer points to something.
I'm wondering what is the best methode in RUST to process arrays of simple data the most efficient way
Hi, zip is still the way to go. Your example can be translated to:
fn add(arr1: &[f32], arr2: &[f32], res: &mut [f32]) {
for (res, (arr1, arr2)) in res.iter_mut().zip(arr1.iter().zip(arr2.iter())) {
*res = *arr1 + *arr2;
}
}
zip
will prevent out-of-bounds access if arr2
or res
is shorter than arr1
as opposed to your example.
LLVM should remove any bound check and vectorize the loop but if it's really important I'd check to see if it's the case.
thanks, Usually those pieces of algo assumes that the user is dealing with buffers of the same size.
It seems that this RUST sample is doing much more that it's C equivalent, I was worrying about performance
At this point isn't doing this a better solution in terms of readability and performance
fn mult_arrays(arr1: &[i32], arr2: &[i32], res: &mut [i32]) {
//supposing I'm sure that all arrays has the same size
for i in 0..arr1.len() {
res[i] = arr1[i] + arr2[i];
}
}
LLVM usually optimizes iterators better than (or the same as) loop + indexing. There is a chapter in the book about it.
You can use itertools::izip - Rust which delagates to .zip
internally and expands the inner tuple.
use itertools::izip;
fn add(arr1: &[f32], arr2: &[f32], res: &mut [f32]) {
for (res, arr1, arr2) in izip!(res.iter_mut(), arr1, arr2) {
*res = *arr1 + *arr2;
}
}