Short-circuit iterator once condition is met - rust

I am trying to write an iterator which conditionally uses elements in a separate iterator. In my example, the separate iterator should increment the sum variable. Once another condition is met *n == 4, the iterator should stop checking the condition and assume rest of elements are increments for the sum variable. I have the following working example:
fn conditional(n: &i64) -> bool {
// a lot of code here which is omitted for brevity
n % 2 == 0
}
fn main() {
let buf = vec![1,2,3,4,5,6];
let mut sum = 0;
let mut iter = buf.iter();
while let Some(n) = iter.next() {
if conditional(n) {
sum += n;
}
if *n == 4 {
// end of file - assume rest of elements are `conditional`
break;
}
};
// rest of elements [5,6]
for n in iter {
sum += n;
}
println!("sum (2+4+5+6): {:?}", sum);
}
output:
sum (2+4+5+6): 17
playground link
I would rather write the same thing with a single iterator using something like flat_map:
fn conditional(n: &i64) -> bool {
// a lot of code here which is omitted for brevity
n % 2 == 0
}
fn main() {
let buf = vec![1,2,3,4,5,6];
let mut sum = 0;
let mut terminate = false;
buf.iter().flat_map(|n| {
if *n == 4 {
// hard terminate here - return Some(n) for rest of iterator [5,6]
terminate = true;
return Some(n);
}
if terminate {
return Some(n);
}
if conditional(n) {
return Some(n);
}
None // odd
})
.for_each(|n| {
sum += n;
});
println!("sum (2+4+5+6): {:?}", sum);
}
output:
sum (2+4+5+6): 17
playground link
Is there a way to write this in a more concise manner? I want to short-circuit the iterator once the *n == 4 condition is reached.

There are many ways to solve this.
Here are a couple:
fn conditional(n: &i64) -> bool {
// a lot of code here which is omitted for brevity
n % 2 == 0
}
fn main() {
let buf = vec![1, 2, 3, 4, 5, 6];
let sum = buf
.iter()
.fold((0, false), |(mut sum, mut terminate), value| {
if *value == 4 {
terminate = true;
}
if terminate || conditional(value) {
sum += *value;
}
(sum, terminate)
})
.0;
println!("sum (2+4+5+6): {:?}", sum);
}
sum (2+4+5+6): 17
Or using filter and a stateful closure:
fn conditional(n: &i64) -> bool {
// a lot of code here which is omitted for brevity
n % 2 == 0
}
fn main() {
let buf = vec![1, 2, 3, 4, 5, 6];
let sum: i64 = buf
.iter()
.filter({
let mut terminate = false;
move |&value| {
terminate || {
if *value == 4 {
terminate = true;
}
conditional(value)
}
}
})
.sum();
println!("sum (2+4+5+6): {:?}", sum);
}
sum (2+4+5+6): 17

You can use filter():
buf.iter().filter(|n| {
if **n == 4 {
terminate = true;
}
terminate || conditional(n)
})
And sum() instead of for_each():
let sum = buf
.iter()
.filter(|n| {
if **n == 4 {
terminate = true;
}
terminate || conditional(n)
})
.sum::<i64>();

Related

compare sum from chunk from array to the previous chunk in rust

I have a let mut arr = vec![100,200,300,400,500,600]; and want to compare chunk of sum
chunkA: 100+200+300 = 600
chunkB: 200+300+400 = 900
compare chunkA and chunkB
To iterate over overlapping chunks in a slice, use windows(). To process pairs of chunks, you can use itertools' tuple_windows():
use itertools::Itertools;
for (prev, current) in v.windows(3).tuple_windows() {
// ...
}
If you want some accumulated result, I'd recommend going with Iterator::fold() (or Iterator::reduce()).
Something like this should do it:
fn main(){
let arr = vec![100,200,300,400,500,600];
println!("{}", arr.windows (3).fold ((None, 0), |(prev, count), w| {
let s = w.iter().sum::<i32>();
(Some (s),
if let Some (prev) = prev {
if s > prev { count + 1 } else { count }
} else {
0
})
}).1);
}
Playground
It uses slice::windows to create the "chunks", then Iterator::fold to process them.
Or equivalently:
fn main() {
let arr = vec![100, 200, 300, 400, 500, 600];
let mut chunks = arr.windows (3);
println!(
"{}",
chunks
.next()
.map(
|first| chunks.fold ((first.iter().sum::<i32>(), 0), |(prev, count), w| {
let s = w.iter().sum::<i32>();
(s, if s > prev { count + 1 } else { count })
})
)
.unwrap_or ((0, 0))
.1
);
}
Playground

Tic Tac Toe - Minimax

I'm trying to build a tic-tac-toe game using minimax algorithm with rust. And I'm stuck. I tried to write a rust code based on the psudeo code on the wikipedia page. https://en.wikipedia.org/wiki/Minimax. However, it didn't work. Ai always makes the first possible move. I would be glad if you could help me.
In main.rs
fn main() {
let mut g = Game::new();
while g.game_state() == Game_State::Continuous {
g.print();
println!("{}", minimax(&g));
if g.turn == Player::Cross {
g.take_input();
}
else {
g = best_move(&g);
}
}
g.print();
if let Game_State::Win(Player::None) = g.game_state() {
println!("Draw");
}
else {
g.print_winner();
}
}
In ai.rs
pub fn child_nodes(game: &Game) -> Vec<Game> {
let mut children: Vec<Game> = Vec::new();
for r in 0..3 {
for c in 0..3 {
if game.grid[r][c] == Player::None {
let mut child = game.clone();
child.grid[r][c] = game.turn;
child.turn = reverse_player(child.turn);
children.push(child);
}
}
}
return children;
}
pub fn minimax(game: &Game) -> isize {
match game.game_state() {
Game_State::Win(winner) => to_scor(winner),
Game_State::Continuous => {
use std::cmp::{min, max};
let children_vec = child_nodes(&game);
let mut score: isize;
if game.turn == Player::Cross {
score = -2;
for i in &children_vec {
score = max(score, minimax(i));
}
}
else {
score = 2;
for i in &children_vec {
score = min(score, minimax(i));
}
}
return score;
}
}
}
pub fn best_move(game: &Game) -> Game {
let children = child_nodes(game);
let mut values: Vec<isize> = Vec::new();
for i in 0..children.len() {
values.push(minimax(&children[i]));
}
let mut index: usize = 0;
let iter = values.iter().enumerate();
if game.turn == Player::Cross {
if let Option::Some(t) = iter.max() {
index = t.0;
}
}
else if game.turn == Player::Circle {
if let Option::Some(t) = iter.min() {
index = t.0;
}
}
let best_pos = children[index];
best_pos
}
pub fn to_scor(x: Player) -> isize {
match x {
Player::Cross => 1,
Player::Circle => -1,
Player::None => 0
}
}
.enumerate() returns an iterator over tuples, and .max() and .min() on an iterator of tuples will compare the tuples - that is, (1, x) is always considered to be less than (2, y) for any values of x and y. This can be demonstrated with this snippet:
fn main() {
let v = vec![3, 1, 2, 5, 3, 6, 7, 2];
println!("{:?}", v.iter().enumerate().min());
println!("{:?}", v.iter().enumerate().max());
}
which prints:
Some((0, 3))
Some((7, 2))
which are just the first and last elements of the list (and not the minimum or maximum elements).
However, as shown here, you can use max_by to use your own function to compare the tuples.

Is there a way to update a string in place in rust?

You can also consider this as, is it possible to URLify a string in place in rust?
For example,
Problem statement: Replace whitespace with %20
Assumption: String will have enough capacity left to accommodate new characters.
Input: Hello how are you
Output: Hello%20how%20are%20you
I know there are ways to do this if we don't have to do this "in place". I am solving a problem that explicitly states that you have to update in place.
If there isn't any safe way to do this, is there any particular reason behind that?
[Edit]
I was able to solve this using unsafe approach, but would appreciate a better approach than this. More idiomatic approach if there is.
fn space_20(sentence: &mut String) {
if !sentence.is_ascii() {
panic!("Invalid string");
}
let chars: Vec<usize> = sentence.char_indices().filter(|(_, ch)| ch.is_whitespace()).map(|(idx, _)| idx ).collect();
let char_count = chars.len();
if char_count == 0 {
return;
}
let sentence_len = sentence.len();
sentence.push_str(&"*".repeat(char_count*2)); // filling string with * so that bytes array becomes of required size.
unsafe {
let bytes = sentence.as_bytes_mut();
let mut final_idx = sentence_len + (char_count * 2) - 1;
let mut i = sentence_len - 1;
let mut char_ptr = char_count - 1;
loop {
if i != chars[char_ptr] {
bytes[final_idx] = bytes[i];
if final_idx == 0 {
// all elements are filled.
println!("all elements are filled.");
break;
}
final_idx -= 1;
} else {
bytes[final_idx] = '0' as u8;
bytes[final_idx - 1] = '2' as u8;
bytes[final_idx - 2] = '%' as u8;
// final_idx is of type usize cannot be less than 0.
if final_idx < 3 {
println!("all elements are filled at start.");
break;
}
final_idx -= 3;
// char_ptr is of type usize cannot be less than 0.
if char_ptr > 0 {
char_ptr -= 1;
}
}
if i == 0 {
// all elements are parsed.
println!("all elements are parsed.");
break;
}
i -= 1;
}
}
}
fn main() {
let mut sentence = String::with_capacity(1000);
sentence.push_str(" hello, how are you?");
// sentence.push_str("hello, how are you?");
// sentence.push_str(" hello, how are you? ");
// sentence.push_str(" ");
// sentence.push_str("abcd");
space_20(&mut sentence);
println!("{}", sentence);
}
An O(n) solution that neither uses unsafe nor allocates (provided that the string has enough capacity), using std::mem::take:
fn urlify_spaces(text: &mut String) {
const SPACE_REPLACEMENT: &[u8] = b"%20";
// operating on bytes for simplicity
let mut buffer = std::mem::take(text).into_bytes();
let old_len = buffer.len();
let space_count = buffer.iter().filter(|&&byte| byte == b' ').count();
let new_len = buffer.len() + (SPACE_REPLACEMENT.len() - 1) * space_count;
buffer.resize(new_len, b'\0');
let mut write_pos = new_len;
for read_pos in (0..old_len).rev() {
let byte = buffer[read_pos];
if byte == b' ' {
write_pos -= SPACE_REPLACEMENT.len();
buffer[write_pos..write_pos + SPACE_REPLACEMENT.len()]
.copy_from_slice(SPACE_REPLACEMENT);
} else {
write_pos -= 1;
buffer[write_pos] = byte;
}
}
*text = String::from_utf8(buffer).expect("invalid UTF-8 during URL-ification");
}
(playground)
Basically, it calculates the final length of the string, sets up a reading pointer and a writing pointer, and translates the string from right to left. Since "%20" has more characters than " ", the writing pointer never catches up with the reading pointer.
Is it possible to do this without unsafe?
Yes like this:
fn main() {
let mut my_string = String::from("Hello how are you");
let mut insert_positions = Vec::new();
let mut char_counter = 0;
for c in my_string.chars() {
if c == ' ' {
insert_positions.push(char_counter);
char_counter += 2; // Because we will insert two extra chars here later.
}
char_counter += 1;
}
for p in insert_positions.iter() {
my_string.remove(*p);
my_string.insert(*p, '0');
my_string.insert(*p, '2');
my_string.insert(*p, '%');
}
println!("{}", my_string);
}
Here is the Playground.
But should you do it?
As discussed for example here on Reddit this is almost always not the recommended way of doing this, because both remove and insert are O(n) operations as noted in the documentation.
Edit
A slightly better version:
fn main() {
let mut my_string = String::from("Hello how are you");
let mut insert_positions = Vec::new();
let mut char_counter = 0;
for c in my_string.chars() {
if c == ' ' {
insert_positions.push(char_counter);
char_counter += 2; // Because we will insert two extra chars here later.
}
char_counter += 1;
}
for p in insert_positions.iter() {
my_string.remove(*p);
my_string.insert_str(*p, "%20");
}
println!("{}", my_string);
}
and the corresponding Playground.

How to swap contents of two indices of a Vec inside a loop?

I'm trying to swap the elements inside a loop.
fn foo(nums: &mut Vec<i32>) {
let mut i: usize = 0;
for (j, n) in nums.iter_mut().enumerate() {
if n != &0 {
// swap nums[i] and nums[j];
i = i + 1;
}
}
}
But I keep getting the same error (along cannot borrow mutable nums multiple times). What am I doing wrong?
Maybe this is not exactly what you want but it may help.
fn main() {
let mut vec = vec![1, 0, 3, 4];
let mut i = 0;
for j in 0..vec.len() {
if vec[j] != 0 {
vec.swap(i, j);
i = i + 1;
}
}
println!("{:?}", vec);
}
Rust Playground

Trying to return an array of string slices in Rust but I'm getting errors that I cannot return values referencing temporary values

My requirement is to return an array of 5 string slices in my calc method.
In my main method, I create any arbitrary 10 element array I want for my program.
I pass this array an a parameter to the calc method, it does some calculations, I am just trying to figure out how I can an array of 5 string slices as I am required to.
As I can return either one of two arrays (of type string slices) based on calculations I get, I am trying to return the appropriate array using an if else if ladder.
fn main()
{
let arr:[u32;10] = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
let output:[&str;5] = calc(arr);
println!("{:?}", output);
}
fn calc(arr: [u32;10]) -> [&'static str; 5]
{
let mut str1:[&str;5] = ["", "", "", "", ""];
let mut str2:[&str;5] = ["", "", "", "", ""];
let mut arr1 = [0; 5];
let mut arr2 = [0; 5];
let mut sorted_arr1 = arr1;
let mut sorted_arr2 = arr2;
sorted_arr1.sort();
sorted_arr2.sort();
for i in 0..5
{
arr1[i] = arr[i];
arr2[i] = arr[i+5];
}
//L for less than, E for equal to, G for greater than
for i in 0..5
{
if arr1[i] < 5 {
str1[i] = &format!("{}{}", arr1[i], "L").to_string();
}
else if arr1[i] == 5 {
str1[i] = &format!("{}{}", arr1[i], "E").to_string();
}
else if arr1[i] > 5 {
str1[i] = &format!("{}{}", arr1[i], "G").to_string();
}
if arr2[i] < 5 {
str2[i] = &format!("{}{}", arr2[i], "L").to_string();
}
else if arr2[i] == 5 {
str2[i] = &format!("{}{}", arr2[i], "E").to_string();
}
else if arr2[i] > 5 {
str2[i] = &format!("{}{}", arr2[i], "G").to_string();
}
}
let foo1_winner = foo1();
let foo2_winner = foo2();
if foo1_winner == sorted_arr1 {
return str1;
}
else if foo1_winner == sorted_arr2 {
return str2;
}
else if foo2_winner == sorted_arr1 {
return str1;
}
else if foo2_winner == sorted_arr2 {
return str2;
}
return str1;
}
fn foo1() -> [u32; 5]
{
[1,2,3,4,5]
}
fn foo2() -> [u32; 5]
{
[6,7,8,9,10]
}
For all my return statements I get an compilation error like this
/*
error[E0515]: cannot return value referencing temporary value
--> stack.rs:71:16
|
43 | str1[i] = &format!("{}{}", arr1[i], "L").to_string();
| ----------------------------------------- temporary value created here
...
71 | return str1;
| ^^^^ returns a value referencing data owned by the current function
*/
I have searched for solutions to this problem but been able to make sense of anything I have read. How do I fix this issue? Any help is appreciated, thanks.

Resources