Complex vector manipulation during iteration [duplicate] - vector

This question already has answers here:
Efficiently mutate a vector while also iterating over the same vector
(2 answers)
Is there an elegant solution to modifying a structure while iterating?
(1 answer)
How to idiomatically iterate one half of an array and modify the structure of the other?
(1 answer)
How can I iterate a vector once and insert/remove/modify multiple elements along the way?
(1 answer)
How can I modify a collection while also iterating over it?
(2 answers)
Closed 4 years ago.
I have a struct GameLog that holds a Vec<Steps>. Steps holds a Vec<FieldData> and FieldData consists only of basic data types.
I create instances of these types using serde_json. After the deserialisation is done, I need to iterate over all the steps in the GameData and add to it the past version of the fields that are changed in the next turn it doesn't already contain.
I have a working Java implementation I'm trying to port to Rust, but I just can't figure this out, probably because I don't know enough about Rust's insides.
The current code looks like this:
let data = load_data("Path/to/json");
let mut gamelog: Vec<Vec<FieldData>> = Vec::with_capacity(data.steps.len() + 1);
gamelog.push(Vec::with_capacity(data.init.fields.len()));
gamelog[0] = data.init.fields;
for i in 1..(data.steps.len() - 1) {
gamelog.push(Vec::with_capacity(data.steps[i - 1].fields.len()));
gamelog[i] = data.steps[i - 1].fields.clone();
for future_field in &data.steps[i + 1].fields {
let mut inside = false;
for current_field in &data.steps[i].fields {
if current_field.x == future_field.x && current_field.y == future_field.y {
inside = true;
}
}
if !inside {
for j in i..0 {
let mut insideb = false;
for past_field in &data.steps[j].fields {
if future_field.x == past_field.x && future_field.y == past_field.y {
gamelog[i].push(past_field.clone());
insideb = true;
break;
}
}
if insideb {
break;
}
}
}
}
}
However, this only works by creating copies of the vectors and fields and creates a new Vec.
When I try to manipulate the Vec directly, I most often get a "can't move out of borrow" error on the for .. in data.steps[?].fields lines
What would a proper (and possibly much more idiomatic) way of directly manipulating the Vecs in the struct?

Related

How to update a struct property in a map [duplicate]

This question already has an answer here:
Golang updating maps and variables in an object
(1 answer)
Closed 3 years ago.
Currently trying to learn Go.
I have the following function, but it only works when the team doesn't exist in the map already and it creates a new record in the map. It will not update the values if the team already has a struct in the map.
func AddLoss(teamMap map[string]TeamRow, teamName string) {
if val, ok := teamMap[teamName]; ok {
val.Wins++
val.GamesPlayed++
} else {
newTeamRow := TeamRow{Losses: 1}
teamMap[teamName] = newTeamRow
}
}
I have updated the function to just replace the existing record with a brand new struct with the values I want, but that seems odd that I can't update the values in a map.
Can someone explain this to me, or point me in the right direction?
You have a map of string to the value of TeamRow so when you get the val out of the map it returns the value of the team, not a pointer to the team. If you make the map a string to the pointer of TeamRow then when you get the val out it will point to the memory that is stored in the map so values will persist beyond the scope of your AddLoss function. To do this simply add a * to the map declaration - teamMap map[string]*TeamRow though when you populate it you will then also need to store pointers in the map.

Appending to a slice that is a value in a map [duplicate]

This question already has an answer here:
Go: append directly to slice found in a map
(1 answer)
Closed 4 years ago.
I want to append to a slice that is a value of a map, e.g. given m map[string][]string:
if values, exists := m[key]; exists {
values = append(values, v)
// I don't want to call: m[key] = values
} else {
m[key] = []string{ v }
}
That obviously doesn't work, so I tried instead of appending the value as is, to do something like:
valuesPtr := &values
*values = append(values, v)
But that doesn't work either. How can I do that?
You cannot do that.
append returns a new slice, since a slice may have to be resized to complete the append. You must update your map to use the newly returned slice, which cannot be done without referencing by key.

Mutating a travelling window in a Rust ndarray

I am attempting to implement one iteration of Conway's Game of Life in Rust using the ndarray library.
I thought a 3x3 window looping over the array would be a simple way to count the living neighbours, however I am having trouble doing the actual update.
The array signifies life with # and the absence of life with :
let mut world = Array2::<String>::from_elem((10, 10), " ".to_string());
for mut window in world.windows((3, 3)) {
let count_all = window.fold(0, |count, cell| if cell == "#" { count + 1 } else { count });
let count_neighbours = count_all - if window[(1, 1)] == "#" { 1 } else { 0 };
match count_neighbours {
0 | 1 => window[(1, 1)] = " ".to_string(), // Under-population
2 => {}, // Live if alive
3 => window[(1, 1)] = "#".to_string(), // Re-produce
_ => window[(1, 1)] = " ".to_string(), // Over-population
}
}
This code does not compile! The error is within the match block with "error: cannot borrow as mutable" and "error: cannot assign to immutable index". I attempted for &mut window... but the library does not implement this(?)
I'm relatively new to Rust and I believe this may be an issue with the implementation of windows by the library. However, I'm not sure and I don't know if there perhaps some variation/fix that allows me to continue with this approach. Do I need to scrap this approach entirely? I'm not sure what the best approach would be here.
Any other suggestions or improvements to the code would be greatly appreciated.
(This code doesn't implement proper rules as I am mutating as I loop and I am ignoring the outer edge, however that is okay in this case. Also, any variations which do these things are also okay - the details are not important.)
Your general approach using ndarray and windows is ok, but the problem is that the values that you get from the windows iterator will always be immutable. You can work around that by wrapping the values in Cell or RefCell, which gives you interior mutability. That is, they wrap a value as if it was immutable, but provide an API to let you mutate it anyway.
Here is your code, fairly brutally adapted to use RefCell:
use ndarray::Array2;
use std::cell::RefCell;
fn main() {
// creating variables for convenience, so they can be &-referenced
let alive = String::from("#");
let dead = String::from(" ");
let world = Array2::<String>::from_elem((10, 10), " ".to_string());
let world = world.map(RefCell::new);
for mut window in world.windows((3, 3)) {
let count_all = window.fold(0, |count, cell| if *cell.borrow() == &alive { count + 1 } else { count });
let count_neighbours = count_all - if *window[(1, 1)].borrow() == &alive { 1 } else { 0 };
match count_neighbours {
0 | 1 => *window[(1, 1)].borrow_mut() = &dead, // Under-population
2 => {}, // Live if alive
3 => *window[(1, 1)].borrow_mut() = &alive, // Re-produce
_ => *window[(1, 1)].borrow_mut() = &alive, // Over-population
}
}
}
What I've done above is really just to get your code working, pretty much as-is. But, as E_net4 pointed out, your solution has a major bug because it is mutating as it reads. Also, in terms of best-practices, your usage of String isn't ideal. An enum is much better because it's smaller, can be stack-allocated and better captures the invariants of your model. With an enum you would derive Copy as below, which would let you use a Cell instead of RefCell, which is likely to be better performance because it copies the data, instead of having to count references.
#[derive(Debug, PartialEq, Clone, Copy)]
enum CellState {
Alive,
Dead
}

Go loop pointer changes [duplicate]

This question already has answers here:
Using Pointers in a for loop
(2 answers)
How to understand this behavior of goroutine?
(2 answers)
Golang Reusing Memory Address Copying from slice?
(2 answers)
Register multiple routes using range for loop slices/map
(1 answer)
Convert slice of string to slice of pointer to string
(2 answers)
Closed 9 months ago.
I am using a for range loop in Go to iterate through a slice of structs.
In each loop, I a pointer to the current item to a variable.
I am confused why the pointer changes value in the next loop.
For example this code:
package main
import "fmt"
type t struct {
val int
}
func main() {
l := []t{{1}, {2}}
var p *t
for _, i := range l {
fmt.Println("begin", p)
p = &i
fmt.Println("end", p)
}
}
I would expect to produce:
begin <nil>
end &{1}
begin &{1}
end &{2}
But actually does:
begin <nil>
end &{1}
begin &{2}
end &{2}
For reference, in my actual code, I am checking for a condition during the loop, and returning the current item and previous one. So I am trying to save a pointer to it, so that in the next iteration it will have access to the previous as well.
The problem is that you're taking the address of the loop/range variable and not the address of the item in slice. However, you're just making a lot of unnecessary work for yourself. For one, why don't you use the i, v := range or better yet i, _ := and then you can do i-1 to get the previous item? Secondly, even if you want it saved in a pointer, still use this syntax and then assign p = &l[i] so you have the address of the item in the slice rather than the address of the loop/range variable.
People are way too eager to use for/each style constructs when it's obviously better to work with the index... If you want index-1 on every iteration, using the index should be your go to way of doing that.
Building off Tim's comment, it seems like you can copy the value on each loop, instead of the pointer, and dereference it after.
package main
import "fmt"
type t struct {
val int
}
func main() {
l := []t{{1}, {2}}
var p t
var i t
for _, i = range l {
fmt.Println("begin", &p)
p = i
fmt.Println("end", &p)
}
}
Another option is to get the pointer to the current item by using the index:
package main
import "fmt"
type t struct {
val int
}
func main() {
l := []t{{1}, {2}}
var p *t
for index, _ := range l {
fmt.Println("begin", p)
p = &l[index]
fmt.Println("end", p)
}
}

Basics in for loop in actionscript 3 in flex

Good Morning stackoverflow...
I'm having a problem.... this is my sample code
var i:Number = new Number();
trace("showarray length" + showArray.length);
for(i=0;i<showArray.length;i++){
trace("equal daw" + showArray.getItemAt(i).id + "==" + num);
if(showArray.getItemAt(i).id == num){
showArray.removeItemAt(i);
}
}
trace('alerts');
myproblem here is...wherenever the if is satisfied it stops looping it immediately goes out of the loop
this is a sample output
given that the length of showArray is 2 and num = 0
showarray length2
equal daw0==0
alerts
please help me
If you want to remove items while iterating over array, iterate in reverse order. This way element removal does not affect cycle condition:
for (var i:int = showArray.length - 1; i >= 0; i--) {
if (someCondition) {
showArray.removeItemAt(i);
}
}
Another small bonus that this is slightly faster, as it doesn't call showArray.length on each step.
An even better way might be to use the filter method of the Array class.
array = array.filter(function (e:*, i:int, a:Array):Boolean {
return e.id != num;
});
When your if is satisfied for id == num (which is 0 so happening in the first loop) and the item is removed, your array length decreases to 1 so the loop won't run any more.
That's because you are removing items at the time you are iterating throught them.
array = [1, 2]
^ // pointer in first iteration
eliminate 1
array = [2]
^ // the pointer remains in the same location
//ups! out of the loop. all items were visited.
You can copy the array before you iterate through it and iterate the copy or mark the indices to remove and remove them later or iterate the array backwards.
PS: Sorry for my poor English.
After showArray.removeItemAt(i);, add i--;
Because you removed the item at index i from the array, the item that was at i + 1 got moved to i. By subtracting one, you ensure that the moved item doesn't get skipped.
alxx's answer is also a good solution.

Resources