Idiomatic way to optionally add one item to an iterator? [duplicate] - rust

Let's say I have:
let it = [1, 2, 3].into_iter();
let jt = [4, 5, 6].into_iter();
let kt = [7, 8, 9].into_iter();
Then I have boolean conditions i, j and k. I want to generate an iterator that conditionally chains it, jt and kt together based on the values of i, j and k. Can I do this with just the built-in Rust Iterator functionality?

You can make Option into an iterator.
let it = i.then_some([1, 2, 3]).into_iter().flatten();
let jt = j.then_some([4, 5, 6]).into_iter().flatten();
let kt = k.then_some([7, 8, 9]).into_iter().flatten();
let iter = it.chain(jt).chain(kt);
If the condition is false, then condition.then_some(...) will return None, making an empty iterator. Otherwise a Some(...) is returned. into_iter().flatten() will transform Option<impl IntoIterator<Item=T>> to impl Iterator<Item=T>.

You're going to run into a slight issue if you want to use bare iterators:
If you write the following:
let iter = [1, 2, 3].into_iter();
let iter = if some_condition {
iter.chain([4, 5, 6])
} else {
iter
}
You'll get an error which boils down to this:
= note: expected struct `std::iter::Chain<std::array::IntoIter<_, _>, std::array::IntoIter<{integer}, 3>>`
found struct `std::array::IntoIter<_, _>`
iter has type IntoIter, but iter.chain() has type Chain<IntoIter, ...>
To get around this, you have a few options:
you can use a trait object, which behaves a bit like an interface from languages like Java, but loses some performance:
let iter = [1, 2, 3].into_iter();
let mut iter: Box<dyn Iterator<Item = i32>> = Box::new(iter);
if some_condition {
iter = Box::new(iter.chain([4, 5, 6]));
}
or, probably a better solution, if you can sacrifice laziness, is to just use a Vec:
// save heap allocations by pre-allocating the whole vec
let len = if some_condition { 6 } else { 3 };
let mut items = Vec::with_capacity(len);
items.extend([1, 2, 3]);
if some_condition {
items.extend([4, 5, 6]);
}

This is a good use for the either crate. Either implements Iterator when both the left and right sides also implement Iterator, so it can be easily used to chain iterators together.
Given any three iterators it, jt, and kt that iterate over the same Item, with accompanying booleans i, j, and k, you can write a function that chains them together like this:
use either::Either;
use std::iter;
fn chain<'a, I, J, K, Item>(
it: I,
jt: J,
kt: K,
i: bool,
j: bool,
k: bool,
) -> iter::Chain<
iter::Chain<Either<I, iter::Empty<Item>>, Either<J, iter::Empty<Item>>>,
Either<K, iter::Empty<Item>>,
>
where
I: Iterator<Item = Item>,
J: Iterator<Item = Item>,
K: Iterator<Item = Item>,
{
let iter = if i {
Either::Left(it)
} else {
Either::Right(iter::empty())
};
let iter = iter.chain(if j {
Either::Left(jt)
} else {
Either::Right(iter::empty())
});
let iter = iter.chain(if k {
Either::Left(kt)
} else {
Either::Right(iter::empty())
});
iter
}
Calling this function will result in an iterator conditional on the input. For example, calling
let it = [1, 2, 3].into_iter();
let jt = [4, 5, 6].into_iter();
let kt = [7, 8, 9].into_iter();
chain(it, jt, kt, true, false, true).collect::<Vec<_>>();
gives
[1, 2, 3, 7, 8, 9]
as expected.
You can try it using this playground.

Related

How to chain arbitrary number of iterables?

I understand some of the jank involving iterables and arrays, but clearly not enough. I want to take any amount of iterables (vectors, arrays, slices, anything implementing IntoIterator) and provide an expected final size, and get an array (i.e. fixed-size) containing the chained values. To clarify, this is mostly for easy refactoring and function calling, so I want this utility to take ownership of the passed iterables and move all their contents into its output, such that:
let a1: u8 = [1, 2, 3];
let a2: u8 = [4, 5, 6];
let joined = join::<u8, 6>([a1, a2, ...]); // [u8; 6]
I tried implementing something with chain, but couldn't get it to work out. I know I can do this unsafely, but I'd rather avoid that if possible. Is there a way to do what I want?
My best (non-working) attempt:
fn join<T, C: IntoIterator<Item = T>, const N: usize>(iterables: Vec<C>) -> [T; N] {
let mut a = vec![].iter().chain(vec![]);
for iterable in iterables {
a = a.chain(iterable.into_iter());
}
a.collect().try_into().unwrap()
}
With credit to #SvenMarnach for simplifying, this problem is neatly solved like so:
use std::convert::TryInto;
fn join<T: Clone, const N: usize>(iterables: Vec<&[T]>) -> [T; N] {
let slice = iterables.concat();
let length = slice.len();
slice.try_into()
.unwrap_or_else(|_| panic!("joined has length {}, expected {}", length, N))
}
Used like so:
fn main() {
let params1 = [1, 2, 3];
let params2 = [4, 5];
print!("sum: {}", sum_six_numbers(join(vec![&params1, &params2])));
}
fn sum_six_numbers(ns: [u8; 5]) -> u8 {
ns.iter().sum()
}

How to get the index of the current element being processed in the iteration without a for loop?

I have read How to iterate a Vec<T> with the indexed position? where the answer is to use enumerate in a for-loop.
But if I don't use a for-loop like this:
fn main() {
let v = vec![1; 10]
.iter()
.map(|&x| x + 1 /* + index */ ) // <--
.collect::<Vec<_>>();
print!("v{:?}", v);
}
How could I get the index in the above closure?
You can also use enumerate!
let v = vec![1; 10]
.iter()
.enumerate()
.map(|(i, &x)| x + i)
.collect::<Vec<_>>();
println!("v{:?}", v); // prints v[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Let's see how this works. Iterator::enumerate returns Enumerate<Self>. That type also implements Iterator:
impl<I> Iterator for Enumerate<I>
where
I: Iterator,
{
type Item = (usize, <I as Iterator>::Item);
// ...
}
As you can see, the new iterator yields tuples of the index and the original
value.
You can simply use enumerate:
fn main() {
let v = vec![1; 10]
.iter()
.enumerate()
.map(|(i, x)| i + x)
.collect::<Vec<_>>();
print!("v{:?}", v);
}
The reason for this is because the for loop takes an enumerator:
In slightly more abstract terms:
for var in expression {
code
}
The expression is an iterator.

How can I insert all values of one HashSet into another HashSet?

I have two HashSet<u16>s and I would like to implement a = a U b. If possible, I'd like to use HashSet::union rather than loops or other tweaks.
I tried the following:
use std::collections::HashSet;
let mut a: HashSet<u16> = [1, 2, 3].iter().cloned().collect();
let b: HashSet<u16> = [7, 8, 9].iter().cloned().collect();
// I can build a union object that contains &u16
let union: HashSet<&u16> = a.union(&b).collect();
// But I can't store the union into a
a = a.union(&b).collect(); // -> compile error
// of course I can do
for x in &b {
a.insert(*x);
}
// but I wonder if union could not be used to simply build a union
The error message is the following:
the trait bound
`std::collections::HashSet<u16>: std::iter::FromIterator<&u16>`
is not satisfied
How can I perform a = a U b?
You don't want union — as you said, it will create a new HashSet. Instead you can use Extend::extend:
use std::collections::HashSet;
fn main() {
let mut a: HashSet<u16> = [1, 2, 3].iter().copied().collect();
let b: HashSet<u16> = [1, 3, 7, 8, 9].iter().copied().collect();
a.extend(&b);
println!("{:?}", a); // {8, 3, 2, 1, 7, 9}
}
(Playground)
Extend::extend is also implemented for other collections, e.g. Vec. The result for Vec will differ because Vec does not honor duplicates in the same way a Set does.
// But I can't store the union into a
a = a.union(&b).collect(); // -> compile error
The error message is the following:
the trait bound `std::collections::HashSet<u16>:
std::iter::FromIterator<&u16>` is not satisfied
It's because a is a HashSet<u16>, but a.union(&b) is an Iterator<Item=&u16>. Converting a.union(&b) to Iterator<Item=u16> by using .copied() works:
a = a.union(&b).copied().collect(); // compiles
As the other mentioned, this will create a new HashSet. In some cases, this might be what you want, but it will make more sense if it's assigned to another variable:
let c: HashSet<u16> = a.union(&b).copied().collect();
// a is unchanged here
For collecting multiple hashsets:
use std::collections::HashSet;
fn main() {
let a: HashSet<u16> = [1, 2, 3].iter().copied().collect();
let b: HashSet<u16> = [1, 3, 7, 8, 9].iter().copied().collect();
let all = [a,b];
let combined = all.iter().flatten().collect::<HashSet<_>>();
println!("{:?}", combined);
}
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=44cd73eb3a4e628378cbb7ff11a32649

Assigning values in a 2D array

I'm writing some code in Rust (mainly as a POC). The code takes a 2D array, passes it to a second function to do some matrix math (I know there is a standard library to do this, but I want to get used to how things work).
The problem is that the assignment to a 2D array is causing an issue.
My code looks like this
fn main()
{
// first create a couple of arrays - these will be used
// for the vectors
let line1: [i32; 4] = [4, 2, 3, 3];
let line2: [i32; 4] = [3, 4, 5, 7];
let line3: [i32; 4] = [2, 9, 6, 2];
let line4: [i32; 4] = [5, 7, 2, 4];
// create two holding arrays and assign
let array_one = [line1, line3, line4, line2];
let array_two = [line2, line1, line3, line4];
// let's do the multiply
let result = matrix_multiply(&array_one, &array_two);
println!("{:?}", result);
}
fn matrix_multiply(vec1:&[&[i32;4];4], vec2:&[&[i32;4];4]) -> [[i32; 4];4]
{
// we need to deference the parameters passed in
let vec_one:[[i32;4];4] = vec1;
let vec_two:[[i32;4];4] = vec2;
// we need to store the sum
let mut sum = 0;
// we need to create the arrays to put the results into
let mut result = [[0i32; 4]; 4];
// loop through the two vectors
for vone in 0..4
{
for vtwo in 0..4
{
for k in 0..4
{
sum = sum + vec1[[vone].k] * vec2[[k].vtwo];
}
result[[vec_one].vec_two] = sum;
sum = 0;
}
}
return result;
}
I've also tried result[vec_one][vec_two] = sum but when I come to compile, it looks like there is a problem assigning to array.
What am I doing wrong here?
Here is your error, I believe (at least one of them):
<anon>:15:34: 15:44 error: mismatched types:
expected `&[&[i32; 4]; 4]`,
found `&[[i32; 4]; 4]`
(expected &-ptr,
found array of 4 elements) [E0308]
<anon>:15 let result = matrix_multiply(&array_one, &array_two);
^~~~~~~~~~
The problem is, reference or dereference of arrays can't go on multiple levels of their nesting. This is because the memory layout of, say, [&[i32; 4]; 4] and [[i32; 4]; 4] is radically different, both in contents and in size - the former array consists of four pointers to other arrays (4*4=16/8*4=32 bytes total, depending on architecture of your machine), while the latter consists of four arrays laid out sequentially (4*4*4=64 bytes total). There is simply no way to go from [[i32; 4]; 4] to &[&[i32; 4]; 4] without rebuilding the outer array, which Rust won't ever do for you because it is too much magic.
You don't really need to use the inner reference; in fact, you probably don't even need to pass these arrays by reference at all: arrays of Copy types are Copy as well, so you can pass them by value. They are small enough to not cause any performance impact, and the compiler will probably optimize it automatically anyway:
fn main() {
// first create a couple of arrays - these will be used
// for the vectors
let line1: [i32; 4] = [4, 2, 3, 3];
let line2: [i32; 4] = [3, 4, 5, 7];
let line3: [i32; 4] = [2, 9, 6, 2];
let line4: [i32; 4] = [5, 7, 2, 4];
// create two holding arrays and assign
let array_one = [line1, line3, line4, line2];
let array_two = [line2, line1, line3, line4];
// let's do the multiply
let result = matrix_multiply(array_one, array_two);
println!("{:?}", result);
}
fn matrix_multiply(vec1: [[i32; 4]; 4], vec2: [[i32; 4]; 4]) -> [[i32; 4]; 4] {
// we need to create the arrays to put the results into
let mut result = [[0i32; 4]; 4];
// loop through the two vectors
for vone in 0..4 {
for vtwo in 0..4 {
let mut sum = 0;
for k in 0..4 {
sum += vec1[vone][k] * vec2[k][vtwo];
}
result[vone][vtwo] = sum;
}
}
result
}
(try it here)
I've also made your code more idiomatic according to the current community practices (braces positioning, spacing, etc.), and I've fixed the weird syntax for accessing arrays.
If you write
let line1: [i32; 4] = [4, 2, 3, 3];
let line2: [i32; 4] = [3, 4, 5, 7];
let line3: [i32; 4] = [2, 9, 6, 2];
let line4: [i32; 4] = [5, 7, 2, 4];
// create two holding arrays and assign
let array_one = [line1, line3, line4, line2];
the type of array_one will be [[i32;4];4] because the line arrays are copied into array_one. Borrowing this via &array_one gives you something of type &[[i32;4];4] which is very different from &[&[T; 4]; 4] (what the function you try to call expects).
Option1 -- Create [&[T; 4]; 4]:
let array_one = [&line1, &line3, &line4, &line2];
some_function(&array_one);
...
fn some_function(matrix: &[&[i32;4];4]) {...}
Option2 -- Change the function sitnature:
let array_one = [line1, line3, line4, line2];
some_function(&array_one);
...
fn some_function(matrix: &[[i32;4];4]) {...}
If you're interested in dealing with multi-dimensional arrays of arbitrary sizes, perhaps you'll find my experimental multiarray crate useful. It basically tries to offer types similar to Box<[T]>, &[T] and &mut[T] for two or more dimensions. Here's an untested example just to get an idea of what I was trying to do:
extern crate multiarray;
use multiarray::*;
fn main() {
// the backing memory will be a dynamically allocated
// linear array with 4 elements in row-major (C-style) order.
let mut matrix = Array2D::new([2, 2], 0i32);
matrix[[0,0]] = 1; matrix[[0,1]] = 2;
matrix[[1,0]] = 3; matrix[[1,1]] = 4;
let mut square = Array2D::new([2, 2], 0i32);
// the borrow methods create reference-like proxies
mat_product(matrix.borrow(), matrix.borrow(),
square.borrow_mut());
}
fn mat_product(a: Array2DRef<i32>, b: Array2DRef<i32>,
mut c: Array2DRefMut<i32>) {
let rows = a.extents()[0]; // extent of 1st dimension
let intr = a.extents()[1]; // extent of 2nd dimension
let cols = b.extents()[1]; // extent of 2nd dimension
assert!(intr == b.extents()[0]);
assert!(rows == c.extents()[0]);
assert!(cols == c.extents()[1]);
for i in 0..rows {
// the i-th row of a and c...
let a_row_i = a.eliminated_dim(0, i);
let mut c_row_i = c.reborrow_mut().eliminated_dim(0, i);
for j in 0..cols {
c_row_i[j] = dot_product(a_row_i, b.eliminated_dim(1, j));
// ^^^j-th column of b^^^
}
}
}
fn dot_product(a: Array1DRef<i32>, b: Array1DRef<i32>) -> i32 {
a.zip(b).fold(0, |acc,(&x,&y)| acc + x * y)
}

How to set a range in a Vec or slice?

My end goal is to shuffle the rows of a matrix (for which I am using nalgebra).
To address this I need to set a mutable range (slice) of an array.
Supposing I have an array as such (let's say it's a 3x3 matrix):
let mut scores = [7, 8, 9, 10, 11, 12, 13, 14, 15];
I have extracted a row like this:
let r = &scores[..].chunks(3).collect::<Vec<_>>()[1];
Now, for the knuth shuffle I need to swap this with another row. What I need to do is:
scores.chunks_mut(3)[0] = r;
however this fails as such:
cannot index a value of type `core::slice::ChunksMut<'_, _>`
Example: http://is.gd/ULkN6j
I ended up doing a loop over and an element by element swap which seems like a cleaner implementation to me:
fn swap_row<T>(matrix: &mut [T], row_src: usize, row_dest: usize, cols: usize){
for c in 0..cols {
matrix.swap(cols * row_src + c, cols * row_dest + c);
}
}
Your code, as you'd like to write it, can never work. You have an array that you are trying to read from and write to at the same time. This will cause you to have duplicated data:
[1, 2, 3, 4]
// Copy last two to first two
[3, 4, 3, 4]
// Copy first two to last two
[3, 4, 3, 4]
Rust will prevent you from having mutable and immutable references to the same thing for this very reason.
cannot index a value of type core::slice::ChunksMut<'_, _>
chunks_mut returns an iterator. The only thing that an iterator is guaranteed to do is return "the next thing". You cannot index it, it is not all available in contiguous memory.
To move things around, you are going to need somewhere temporary to store the data. One way is to copy the array:
let scores = [7, 8, 9, 10, 11, 12, 13, 14, 15];
let mut new_scores = scores;
for (old, new) in scores[0..3].iter().zip(new_scores[6..9].iter_mut()) {
*new = *old;
}
for (old, new) in scores[3..6].iter().zip(new_scores[0..3].iter_mut()) {
*new = *old;
}
for (old, new) in scores[6..9].iter().zip(new_scores[3..6].iter_mut()) {
*new = *old;
}
Then it's a matter of following one of these existing questions to copy from one to the other.
that's probably closer to what You wanted to do:
fn swap_row<T: Clone>(matrix: &mut [T], row_src: usize, row_dest: usize, cols: usize) {
let v = matrix[..].to_vec();
let mut chunks = v.chunks(cols).collect::<Vec<&[T]>>();
chunks.swap(row_src, row_dest);
matrix.clone_from_slice(chunks.into_iter().fold((&[]).to_vec(), |c1, c2| [c1, c2.to_vec()].concat()).as_slice());
}
I would prefer:
fn swap_row<T: Clone>(matrix: &[T], row_src: usize, row_dest: usize, cols: usize) -> Vec<T> {
let mut chunks = matrix[..].chunks(cols).collect::<Vec<&[T]>>();
chunks.swap(row_src, row_dest);
chunks.iter().fold((&[]).to_vec(), |c1, c2| [c1, c2.to_vec()].concat())
}
btw: nalgebra provides unsafe fn as_slice_unchecked(&self) -> &[T] for all kinds of Storage and RawStorage.
Shuffeling this slice avoids the need for row swapping.

Resources