While reading shallow copy. It says that copy.copy(x) create shallow copy. But I don't see shallow copy behavior in case of the single dimensional list.
Example:
>> new = [1,2,3,4,5,6]
>> original = copy.copy(new)
>> new
[1, 2, 3, 4, 5, 6]
>> original
[1, 2, 3, 4, 5, 6]
>> id(new)
65022912
>> id(original)
65022512
>> new[2]=13
>> new
[1, 2, 13, 4, 5, 6]
>> original
[1, 2, 3, 4, 5, 6]
So here I assume updating "new" list should update "original" list but it is not happening.
In the case of the multidimensional list, the shallow copy is working properly.
Example:
>> parent_list = [1, 2, [3,4], [5,6]]
>> child_list = copy.copy(parent_list)
>> parent_list[2][1] = "Python"
>> parent_list
[1, 2, [3, 'Python'], [5, 6]]
>> child_list
[1, 2, [3, 'Python'], [5, 6]]
Please guide me, why the shallow copy is not working in case of a one-dimensional list.
Thanks.
There are actually 3 "Levels" to what you want.
1) Create a new reference to the same list. This aliasing is a trivial operation and would look like "original = new" or something like that. original[1] =x will update new[1]; This is the equivalent of copying a pointer in a pointer based language.
original = new
original[1] = x
new[1] will now be updated to x
This should be obvious but for completeness, If you follow the above with:
new = other
original is NOT affected at all.
2) Create a "Copy" of the list. It will allocate a new area and copy elements of the list. This is a "Shallow" copy. The children copied will be references, but the list itself will contain copies. original[1] =x will NOT update new[1], but original[1].childValue =x will update new[1].childValue
original = shallow copy of new
original[1].value = x
new[1].value WILL change to x
original[1] = y
new[1] will NOT be affected
3) Create a deep copy of the list. This will allocate a new area and shallow copy the list, but then will recurse and copy each child referenced in the list. No updates to original will modify new, or vice-a-versa.
original = deep copy of new
original[1].value = x
new[1].value will NOT be affected
original[1] = y
new[1] will NOT be affected
A shallow copy of a list is not usually what you want because your list is left in a hybrid state with some members referencing other lists and some not which will lead to unpredictable behavior, but it may be necessary if your tree is fairly deep and/or you never modify the child nodes.
original[1].value = x is giving error :AttributeError: 'int' object has no attribute 'values'
After writing the below code:
new = [1,2,3,4,5,6]
original = new.copy()
original[1] = 10
print("new:",new , "\n " ,"original:", original)
the output I am getting is :
new: [1, 2, 3, 4, 5, 6]
original: [1, 10, 3, 4, 5, 6]
changes done in the original are not getting reflected for a new list where an index is the same or a shallow copy
Related
Let's say I have a vector, the values are from 1 to 10. I want that if you find 5 and 5 next to each other, remove them together with the next elements.
input
[1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10]
expected output
[1, 2, 3, 4]
This was my attempt. I'm finding index to remove, but borrowing rules are making me stuck.
let mut element = vec![1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10];
for (index, val) in element.iter().enumerate() {
if *val == 5 {
if let Some(next_val) = element.get(index + 1) {
if *next_val == 5 {
//element.drain(index..);
}
}
}
}
Rust is saving you from iterator invalidation (a common source of bugs in other languages). This is an error that usually happens when you try to modify a data structure while concurrently iterating over it. You cannot move on to the (now-deleted) next element after calling element.drain(index..). So you need to add a break after that point to avoid memory unsafety.
In this case just adding break; is sufficient to make the code compile. However, for a more concise, linear solution, take full advantage of the iterators and methods provided by the standard library:
if let Some(index) = element.windows(2).position(|pair| pair[0] == pair[1]) {
element.truncate(index);
}
windows(2) on a slice gives an iterator over subslices of length 2, and the position call returns the index of the first element of that iterator for which the two elements of the slice are equal. (If no such pair exists, position returns None.)
I find that the position closure becomes more obvious with the (currently unstable) array_windows feature:
if let Some(index) = element.array_windows().position(|[x, y]| x == y) {
element.truncate(index);
}
Playground
Related
is it possible to filter on a vector in-place?
You can't do what you want to do because you want to remove some elements from a vector while you are iterating it. And this is a big mistake. Note that removing any elements from a vector invalidates the iterators, hence you will access unexpected locations so rust doesn't allow UBs
You can use something like the following
let mut elements = vec![1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10];
let mut first_repeated_five_index_op = None;
for index in 0..elements.len()-1{
if elements[index] == 5 && elements[index + 1] == 5{
first_repeated_five_index_op = Some(index);
break;
}
}
if let Some(first_repeated_five_index) = first_repeated_five_index_op{
elements.truncate(first_repeated_five_index);
}
println!("{:?}", elements);
See a Demo
I have an information on a global list variables. I want to use this information and make some manipulation with it. But I don’t want to change this global list. Right now any manipulation I make change this global list. I have already a long code code with this global list.
The following example represents my bug difficulty:
function Dont_Change_Global_List(L)
Manipulating_List = L
deleteat!(Manipulating_List,2)
return Manipulating_List
end
global s = [1,2,3,4,5]
println("function result: ",Dont_Change_Global_List(s))
println(“global list”, s)
I get:
function result: [1, 3, 4, 5]
global list[1, 3, 4, 5]
I need the following result:
function result: [1, 3, 4, 5]
global list[1, 2, 3, 4, 5]
Is there anything I can do without changing the global list s but still make a manipulations?
You can copy the list internally:
function dont_change_list(L)
Manipulating_List = copy(L)
deleteat!(Manipulating_List,2)
return Manipulating_List
end
I'm implementing a function where I'll be repeatedly eliminating values from a large list, and passing a copy of this list as a vector into another function each iteration:
let mut v = vec![5, 4, 4, 2, 6, 5, 1, 8, 2, 1, 6, 5, 4, 2, 0, 1];
for i in 0..10 {
println!("{}", Vector::from(v).iter().sum());
v.retain(|x| x > i);
}
If v is very large, this will be slow. Is there a better way? I tried:
let mut v = vec![5, 4, 4, 2, 6, 5, 1, 8, 2, 1, 6, 5, 4, 2, 0, 1];
let mut v = v.into_iter().map(|x| Some(x)).collect();
(and then replace the "deleted" values with None) but this just seemed unwieldy to convert to and from an ordinary Vec.
How should I be storing this list of values?
You can restructure your creation of the copied list to do the removal before the copy:
for i in 0..10 {
let dup = your_list.iter().filter(|n| n > i).collect::<Vec<_>>();
use_it(dup);
}
If it is important to your use case that you are left with a filtered Vec, and cannot change the collection type, then this is probably the most useful means. If the filters are cumulative, you can overwrite the original Vec with the filtered Vec each iteration to reduce the workload for each future iteration.
let mut list = your_list;
for i in 0..10 {
list = list.iter().filter(|n| n > i).collect();
use_it(list.clone());
}
The question you asked is directly answered by reshaping how you filter and duplicate the vector, but if you are able to change your types, then the answers below may be more worthwhile.
If your use_it function does not require a Vec or slice, then you may be better served by restructuring the consumer to take an iterator of numbers, and passing in your_list.iter().filter(...). This will result in no copying or rearranging in memory, and the consumer function will just skip the invalid values.
If you care more about counting how many times numbers appear in a collection, and do not specifically need a sequential list in memory, you can rearrange your list into a HashMap:
use std::collections::HashMap;
let mut dict: HashMap<i32, usize> = HashMap::new();
for num in your_list {
*dict.entry(num).or_insert(0) += 1;
}
and then you can filter numbers out of the map with constant-time access rather than linear-time in the size of the collection.
Since this is a question about performance, then you will need to benchmark everything so that you can test your assumptions. That being said and unless there's something smart to do inside the function you call (maybe only copying lazily the items you want to mutate), then I think your retain+clone approach is close to the fastest you can do. Using Options is almost certainly a bad idea: it adds checks everywhere and it kills cache locality.
The only thing that may improve performance is to do the copy and filtering in a single pass:
let mut v = vec![5, 4, 4, 2, 6, 5, 1, 8, 2, 1, 6, 5, 4, 2, 0, 1];
let mut work = v.clone();
for i in 0..10 {
println!("{}", work.iter().sum::<i32>());
work.clear();
v.retain(|&x| if (x > i) { work.push (x); true } else { false });
}
playground
Note that this will probably not make any difference if your data fits in the cache. In any case, benchmark, benchmark, benchmark! Lots of assumptions get proven wrong in the face of compiler optimizations and modern CPU architecture.
If you're removing the elements in order you should consider a queue. Using remove() takes O(1) time to remove an element, because it is essentially a dequeue or a pop.
Is there a Qore operator/function to get sublist from a list without modifying source list, i.e. equivalent of substr(). extract operator removes items from original list.
list l = (1,2,3,4,5,6,7,8,9);
list l2 = extract l, 2, 4;
printf("l:%y\nl2:%y\n", l, l2);
l:[1, 2, 7, 8, 9]
l2:[3, 4, 5, 6]
select operator supports in condition argument $# macro expanded as index.
list l = (1,2,3,4,5,6,7,8,9);
list l2 = select l, $# >= 2 && $# <2+4;
printf("l:%y\nl2:%y\n", l, l2);
l:[1, 2, 3, 4, 5, 6, 7, 8, 9]
l2:[3, 4, 5, 6]
The select operator is the best solution as you stated in your answer to your own question.
The splice and extract operators both will modify the list operand, which is not what you want.
Note that there is an outstanding feature issue for this in Qore (1781) - not yet targeted to a release, but it could go in the next major release (0.8.13) if there is any interest.
Suppose I have a tuple of (1, 2, 3) and want to index a multidimensional array with it such as:
index = (1, 2, 3)
table[index] = 42 # behaves like table[1][2][3]
index has an unknown number of dimensions, so I can't do:
table[index[0]][index[1]][index[2]]
I know I could do something like this:
functools.reduce(lambda x, y: x[y], index, table)
but it's utterly ugly (and maybe also inefficient), so I wonder if there's a better, more Pythonic choice.
EDIT: Maybe a simple loop is best choice:
elem = table
for i in index:
elem = elem[i]
EDIT2: Actually, there's a problem with both solutions: I can't assign a value to the indexed array :-(, back to ugly:
elem = table
for i in index[:-1]:
elem = elem[i]
elem[index[-1]] = 42
The question is very interesting and also your suggested solution looks good (havn't checked it, but this kind of problem requires a recursive treatment and you just did it in one line).
However, the pythonic way I use in my programs is to use dictionaries of tuples. The syntax is array-like, the performance - of a dictionary, and there was no problem in it for me.
For example:
a = {(1, 2, 3): 'A', (3, 4, 5): 'B', (5, 6, 7, 8): 'C'}
print a[1, 2, 3]
print a[5, 6, 7, 8]
Will output:
A
B
And assigning to an index is super easy:
a[1, 4, 5] = 42. (But you might want to first check that (1, 4, 5) is within the dict, or else it will be created by the assignment)