How can I iterate over a sequence multiple times within a function? - rust

I have a function that I would like to take an argument that can be looped over. However I would like to loop over it twice. I tried using the Iterator trait however I can only iterate over it once because it consumes the struct when iterating.
How should I make it so my function can loop twice? I know I could use values: Vec<usize> however I would like to make it generic over any object that is iterable.
Here's an example of what I would like to do: (Please ignore what the loops are actually doing. In my real code I can't condense the two loops into one.)
fn perform<'a, I>(values: I) -> usize
where
I: Iterator<Item = &'a usize>,
{
// Loop one: This works.
let sum = values.sum::<usize>();
// Loop two: This doesn't work due to `error[E0382]: use of moved value:
// `values``.
let max = values.max().unwrap();
sum * max
}
fn main() {
let v: Vec<usize> = vec![1, 2, 3, 4];
let result = perform(v.iter());
print!("Result: {}", result);
}

You can't iterate over the same iterator twice, because iterators are not guaranteed to be randomly accessible. For example, std::iter::from_fn produces an iterator that is most definitely not randomly accessible.
As #mousetail already mentioned, one way to get around this problem is to expect a Cloneable iterator:
fn perform<'a, I>(values: I) -> usize
where
I: Iterator<Item = &'a usize> + Clone,
{
// Loop one: This works.
let sum = values.clone().sum::<usize>();
// Loop two: This doesn't work due to `error[E0382]: use of moved value:
// `values``.
let max = values.max().unwrap();
sum * max
}
fn main() {
let v: Vec<usize> = vec![1, 2, 3, 4];
let result = perform(v.iter());
println!("Result: {}", result);
}
Result: 40
Although in your specific example, I'd compute both sum and max in the same iteration:
fn perform<'a, I>(values: I) -> usize
where
I: Iterator<Item = &'a usize>,
{
let (sum, max) = values.fold((0, usize::MIN), |(sum, max), &el| {
(sum + el, usize::max(max, el))
});
sum * max
}
fn main() {
let v: Vec<usize> = vec![1, 2, 3, 4];
let result = perform(v.iter());
println!("Result: {}", result);
}
Result: 40

Related

Problems trying to get intersection of a Vector

I am having a problem whilst trying to get the intersection of two Vectors.
impl Solution {
pub fn intersection(nums: Vec<Vec<i32>>) -> Vec<i32> {
// Intended strategy:
// Store the first element into an intersect_result
// Iterate over the remaining elements for each element:
// Determine the intersection of the current element and the intersect result
// set intersect result to this.
// Sort the intersect result
// Return the intersect result back to the caller.
let len:i32 = nums.len() as i32;
let intersect_result:Vec<i32> = nums[0].clone();
for i in 1..len{
println!("i is: {}", i);
let temp_vec:Vec<i32> = nums[i as usize].clone();
// find the intersection of the current element with the intersect result
let unique_a:HashSet<i32> = temp_vec.into_iter().collect();
let unique_b:HashSet<i32> = intersect_result.clone().into_iter().collect();
intersect_result = unique_a.intersection(&unique_b).collect::<Vec<_>>();
}
vec![]
}
}
The error message I get is:
= note: expected struct `Vec<i32>`
found struct `Vec<&i32>`
This happens in the call unique_a.intersection().
Any thoughts guys?
You can add a map(|i| *i) in the iterator chain that causes the error:
intersect_result = unique_a.intersection(&unique_b).map(|i| *i).collect::<Vec<_>>();
When fixing this, the code also seems to work as intended. I think there are a few improvements possible (probably more, but these immediately tracked my attention):
use hashbrown::HashSet;
pub fn intersection(nums: Vec<Vec<i32>>) -> Vec<i32> {
let mut intersect_result: Vec<i32> = nums[0].clone();
for temp_vec in nums {
let unique_a: HashSet<i32> = temp_vec.into_iter().collect();
intersect_result = unique_a
.intersection(&intersect_result.into_iter().collect())
.map(|i| *i)
.collect::<Vec<_>>();
}
intersect_result
}
fn main() {
let a = vec![1, 2, 3];
let b = vec![2, 3, 4];
let c = vec![3, 4, 5];
let v = vec![a, b, c];
let res = intersection(v);
println!("res: {:?}", res);
}

Is there a way to shuffle two or more lists in the same order?

I want something like this pseudocode:
a = [1, 2, 3, 4];
b = [3, 4, 5, 6];
iter = a.iter_mut().zip(b.iter_mut());
shuffle(iter);
// example shuffle:
// a = [2, 4, 3, 1];
// b = [4, 6, 5, 3];
More specifically, is there some function which performs like:
fn shuffle<T>(iterator: IterMut<T>) { /* ... */ }
My specific case is trying to shuffle an Array2 by rows and a vector (array2:Lndarray:Array2<f32>, vec:Vec<usize>).
Specifically array2.iter_axis(Axis(1)).zip(vec.iter()).
Shuffling a generic iterator in-place is not possible.
However, it's pretty easy to implement shuffling for a slice:
use rand::Rng;
pub fn shufflex<T: Copy>(slice: &mut [T]) {
let mut rng = rand::thread_rng();
let len = slice.len();
for i in 0..len {
let next = rng.gen_range(i, len);
let tmp = slice[i];
slice[i] = slice[next];
slice[next] = tmp;
}
}
But it's also possible to write a more general shuffle function that works on many types:
use std::ops::{Index, IndexMut};
use rand::Rng;
pub fn shuffle<T>(indexable: &mut T)
where
T: IndexMut<usize> + Len + ?Sized,
T::Output: Copy,
{
let mut rng = rand::thread_rng();
let len = indexable.len();
for i in 0..len {
let next = rng.gen_range(i, len);
let tmp = indexable[i];
indexable[i] = indexable[next];
indexable[next] = tmp;
}
}
I wrote a complete example that also allows shuffling across multiple slices in the playground.
EDIT: I think I misunderstood what you want to do. To shuffle several slices in the same way, I would do this:
use rand::Rng;
pub fn shuffle<T: Copy>(slices: &mut [&mut [T]]) {
if slices.len() > 0 {
let mut rng = rand::thread_rng();
let len = slices[0].len();
assert!(slices.iter().all(|s| s.len() == len));
for i in 0..len {
let next = rng.gen_range(i, len);
for slice in slices.iter_mut() {
let tmp: T = slice[i];
slice[i] = slice[next];
slice[next] = tmp;
}
}
}
}
To shuffle in the same order, you can first remember the order and then reuse it for every shuffle. Starting with the Fisher-Yates shuffle from the rand crate:
fn shuffle<R>(&mut self, rng: &mut R)
where R: Rng + ?Sized {
for i in (1..self.len()).rev() {
self.swap(i, gen_index(rng, i + 1));
}
}
It turns out that we need to store random numbers between 0 and i + 1 for each i between 1 and the length of the slice, in reverse order:
// create a vector of indices for shuffling slices of given length
let indices: Vec<usize> = {
let mut rng = rand::thread_rng();
(1..slice_len).rev()
.map(|i| rng.gen_range(0, i + 1))
.collect()
};
Then we can implement a variant of shuffle where, instead of generating new random numbers, we pick them up from the above list of random indices:
// shuffle SLICE taking indices from the provided vector
for (i, &rnd_ind) in (1..slice.len()).rev().zip(&indices) {
slice.swap(i, rnd_ind);
}
Putting the two together, you can shuffle multiple slices in the same order using a method like this (playground):
pub fn shuffle<T>(slices: &mut [&mut [T]]) {
if slices.len() == 0 {
return;
}
let indices: Vec<usize> = {
let mut rng = rand::thread_rng();
(1..slices[0].len())
.rev()
.map(|i| rng.gen_range(0, i + 1))
.collect()
};
for slice in slices {
assert_eq!(slice.len(), indices.len() + 1);
for (i, &rnd_ind) in (1..slice.len()).rev().zip(&indices) {
slice.swap(i, rnd_ind);
}
}
}

Why do I get "no method named `join` found for type `[usize]` in the current scope"?

fn example(books: Vec<usize>, n_books: usize) {
let fbooks = books[0..n_books].join(" ");
}
error[E0599]: no method named `join` found for type `[usize]` in the current scope
--> src/lib.rs:2:36
|
2 | let fbooks = books[0..n_books].join(" ");
| ^^^^ method not found in `[usize]`
I tried mapping usize to String, collecting into Vec and some more random stuff. I don't understand what's going on.
TLDR:
Since map() transforms one iterator into another iterator, you should provide an iterator first.
Try this:
fn example(books: Vec<usize>, n_books: usize) -> String {
books[0..n_books]
.iter()
.map(|u| u.to_string())
.collect::<Vec<_>>()
.join(" ")
}
fn main() {
let books: Vec<usize> = vec![1, 2, 3];
let n_books: usize = 2;
let result = example(books, n_books);
println!("{}", result); //"1 2"
}
Notes:
You should not join the usize vector items with &str type. You need to convert usize to string first then join it:
map(|u| u.to_string())
The standard library's join is defined only for slices, so the following code works:
let books: &[_] = &["1", "2", "3"];
let n_books: usize = 2;
let result = books[0..n_books].join(" ");
println!("{}", result); // "1 2"
Edit:
From your comment:
What's the difference using take(n) or a subscript operator (re-slicing books[0..n_books])
A slice is a type composed of a length and a pointer to a memory.
You may use .take(n_books) instead of re-slicing, which is an iterator that only iterates over the first n iterations of iter, try this:
let books: Vec<usize> = vec![1, 2, 3];
let n_books: usize = 2;
let result = books
.iter()
.take(n_books)
.map(|u| u.to_string())
.collect::<Vec<_>>()
.join(" ");
println!("{}", result); //"1 2"

How can I write a Rust function to find different characters between two strings?

The order of the characters is not important but the count is. I mean aaabaaa equals to 6a + b and the function is like math subtraction. For example:
fn diff(a: String, b: String) -> String {}
diff("aabbac", "accba") => "ab"
---------------------------------
"aabbac" = (3a+2b+c)
"accba" = (2a+b+2c)
(3a+2b+c) - (2a+b+2c) = a+b // -c is ignored
The usual technique is to create a function that counts the number of occurrences of each char, like collections.Counter in Python, and to compare these numbers for strings a and b.
The Rust standard library documentation contains a snippet that does the job. This is an adaptation that accepts any iterator:
use std::collections::HashMap;
use std::hash::Hash;
use std::iter::Iterator;
fn counter<T, I>(it: I) -> HashMap<T, usize>
where
T: Eq + Hash,
I: Iterator<Item = T>,
{
let mut count_by_element = HashMap::new();
for e in it {
*count_by_element.entry(e).or_insert(0) += 1;
}
count_by_element
}
Now that we know how to build a map char -> count, we just have to compare the counts of the string a and b:
use std::iter;
fn diff(a: &str, b: &str) -> String {
let mut v: Vec<char> = vec![];
let counter_a = counter(a.chars());
let counter_b = counter(b.chars());
for (c, n_a) in &counter_a {
let n_b = counter_b.get(c).unwrap_or(&0); // how many `c` in `b`?
if n_a > n_b {
v.extend(iter::repeat(c).take(n_a - n_b)); // add `n_a - n_b` `c`s
}
}
v.into_iter().collect::<String>() // build the String
}
If you want a "one shot" function, you can forget the counter function and use a more direct approach:
fn diff_one_shot(a: &str, b: &str) -> String {
let mut counter = HashMap::new();
for c in a.chars() {
*counter.entry(c).or_insert(0) += 1; // one more
}
for c in b.chars() {
*counter.entry(c).or_insert(0) -= 1; // one less
}
counter
.iter()
.filter(|(_c, &n)| n > 0) // only if more `c` in `a` than in `b`
.flat_map(|(c, &n)| iter::repeat(c).take(n)) // `n` times `c`
.collect::<String>()
}
Examples:
fn main() {
println!("{:?}", counter("aaabbc".chars()));
// {'b': 2, 'c': 1, 'a': 3}
println!("{}", diff("aaabbc", "ab"));
//aabc
println!("{}", diff_one_shot("aaabbc", "ab"));
//aacb
}

String join on strings in Vec in reverse order without a `collect`

I'm trying to join strings in a vector into a single string, in reverse from their order in the vector. The following works:
let v = vec!["a".to_string(), "b".to_string(), "c".to_string()];
v.iter().rev().map(|s| s.clone()).collect::<Vec<String>>().connect(".")
However, this ends up creating a temporary vector that I don't actually need. Is it possible to do this without a collect? I see that connect is a StrVector method. Is there nothing for raw iterators?
I believe this is the shortest you can get:
fn main() {
let v = vec!["a".to_string(), "b".to_string(), "c".to_string()];
let mut r = v.iter()
.rev()
.fold(String::new(), |r, c| r + c.as_str() + ".");
r.pop();
println!("{}", r);
}
The addition operation on String takes its left operand by value and pushes the second operand in-place, which is very nice - it does not cause any reallocations. You don't even need to clone() the contained strings.
I think, however, that the lack of concat()/connect() methods on iterators is a serious drawback. It bit me a lot too.
I don't know if they've heard our Stack Overflow prayers or what, but the itertools crate happens to have just the method you need - join.
With it, your example might be laid out as follows:
use itertools::Itertools;
let v = ["a", "b", "c"];
let connected = v.iter().rev().join(".");
Here's an iterator extension trait that I whipped up, just for you!
pub trait InterleaveExt: Iterator + Sized {
fn interleave(self, value: Self::Item) -> Interleave<Self> {
Interleave {
iter: self.peekable(),
value: value,
me_next: false,
}
}
}
impl<I: Iterator> InterleaveExt for I {}
pub struct Interleave<I>
where
I: Iterator,
{
iter: std::iter::Peekable<I>,
value: I::Item,
me_next: bool,
}
impl<I> Iterator for Interleave<I>
where
I: Iterator,
I::Item: Clone,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
// Don't return a value if there's no next item
if let None = self.iter.peek() {
return None;
}
let next = if self.me_next {
Some(self.value.clone())
} else {
self.iter.next()
};
self.me_next = !self.me_next;
next
}
}
It can be called like so:
fn main() {
let a = &["a", "b", "c"];
let s: String = a.iter().cloned().rev().interleave(".").collect();
println!("{}", s);
let v = vec!["a".to_string(), "b".to_string(), "c".to_string()];
let s: String = v.iter().map(|s| s.as_str()).rev().interleave(".").collect();
println!("{}", s);
}
I've since learned that this iterator adapter already exists in itertools under the name intersperse — go use that instead!.
Cheating answer
You never said you needed the original vector after this, so we can reverse it in place and just use join...
let mut v = vec!["a".to_string(), "b".to_string(), "c".to_string()];
v.reverse();
println!("{}", v.join("."))

Resources