Update and store a global Julia array with !push - arrays

I'm a bit puzzled about the way that Julia 1.0.3 treats global variables. Is there a way to use !push to update a global array?
While playing in the REPL, I want to update a global variable, then push! the result to a global array to store it.
var = [1]
res = []
for i in 1:5
global var
global res
push!(var,i)
print(string(var,"\n"))
push!(res,var)
end
However, the values stored in res are as follows:
[1, 1, 2, 3, 4, 5]
[1, 1, 2, 3, 4, 5]
[1, 1, 2, 3, 4, 5]
[1, 1, 2, 3, 4, 5]
[1, 1, 2, 3, 4, 5]
Whereas I would expect this:
[1, 1]
[1, 1, 2]
[1, 1, 2, 3]
[1, 1, 2, 3, 4]
[1, 1, 2, 3, 4, 5]
Particularly puzzling since behaviour seems as expected with variables, instead of arrays:
var = 1
res = []
for i in 1:5
global var
global res
var = var + i
print(string(var,"\n"))
push!(res, var)
end
Which gives expected result:
2
4
7
11
16
I am clearly missing something.

You're pushing the same var array to every spot in the res array. For example:
julia> var = [1]
1-element Array{Int64,1}:
1
julia> res = [var, var]
2-element Array{Array{Int64,1},1}:
[1]
[1]
julia> var[1] = 2
2
julia> res
2-element Array{Array{Int64,1},1}:
[2]
[2]
Both elements in the res array are var itself. So if you modify var (with push! or indexed assignment or somesuch), then no matter how you access it you'll see those modifications.
This doesn't occur with numbers because you cannot modify numbers themselves. You can change which number is stored in an array, but you cannot change the number 1 to represent 2 everywhere that 1 had previously been used — that's the equivalent of what's happening here.
To fix this, you'll often want to just create your var array inside the for loop (instead of outside it). But in this case, since you're iteratively adding things to var and want to save that intermediate state, you can use copy:
julia> for i in 1:5
global var
global res
push!(var,i)
print(string(var,"\n"))
push!(res,copy(var))
end
Any[1]
Any[1, 2]
Any[1, 2, 3]
Any[1, 2, 3, 4]
Any[1, 2, 3, 4, 5]
julia> res
5-element Array{Any,1}:
Any[1]
Any[1, 2]
Any[1, 2, 3]
Any[1, 2, 3, 4]
Any[1, 2, 3, 4, 5]

Related

Sorting an array according to an element inside this array

I need to order my array according to an element of it, the reference element can vary.
For example, I would like the 3 to become the first element of the array and the 1, 2 to be put at the end.
array = [1, 2, 3, 4, 5, 6]
new_array = [3, 4, 5, 6, 1, 2]
The element may vary. If I start from 5, the behavior must be the same: the elements that precede are placed at the end, so I will have :
new_array = [5, 6, 1, 2, 3, 4]
If I understand correctly you want to rotate the array.
array
# [1, 2, 3, 4, 5, 6]
array.rotate(2) # or array.rotate(array.index(3))
# [3, 4, 5, 6, 1, 2]
https://apidock.com/ruby/v2_5_5/Array/rotate
Definitely use #rotate for this in actual use, but as an alternative, you could do something like #shift and #push until the desired element is at the beginning of the array.
def rotate(arr, elem)
arr2 = arr.clone
arr2.push(arr2.shift) until arr2.first == elem
arr2
end
irb(main):026:0> arr = [1, 2, 3, 4, 5, 6]
=> [1, 2, 3, 4, 5, 6]
irb(main):027:0> rotate(arr, 3)
=> [3, 4, 5, 6, 1, 2]
irb(main):028:0> arr
=> [1, 2, 3, 4, 5, 6]
Clearly, if elem is not in arr, this will run forever. You could implement some kind of check to ensure this doesn't happen, but that's just one reason you shouldn't actually do this as anything other than a learning exercise.
One approach would be to find the index of elem in arr and shift/push that many times. The &. operator may be useful in that situation to deal with the possibility of not finding elem in arr.

How does python list.sort() function actually work?

Look at the following code:
arr = [5, 4, 3, 2, 1]
arr1 = arr
arr1.sort()
print(arr, arr1)
The expected output is:
[5, 4, 3, 2, 1] [1, 2, 3, 4, 5]
As, arr1 is getting sorted and not arr.
Although, the output is:
[1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
How come both lists are getting sorted?
sort() is in-place function in python and lists are pass by reference. So, if one array is getting sorted in-place it will change another, too.
If you want to prevent that, you can use:
arr = arr1[:]
or
import copy
arr = copy.deepcopy(arr1)

How to get a method to run on an array

I have been doing code Katas on codewars and I wanted to see if I could run them on my computer. But while I can devise various methods I can’t get any of them to run using Command Prompt with Ruby. E.g.
def sum_array(arr)
if arr.kind_of?(Array) and arr.length > 2
arr.inject(:+) - arr.min - arr.max
else
0
end
end
puts sum_array = [1, 2, 3, 4]
Instead of returning 5 it just gives me the unmodified array.
What is the correct way for me to get the method to work on an array?
Think = sign as 'get'.
By writing sum_array = [1, 2, 3, 4], you're assigning the [1, 2, 3, 4] to sum_array instead of calling it, which is why it puts out 1, 2, 3, 4.
To call that method with [1, 2, 3, 4] as argument, do:
puts sum_array([1, 2, 3, 4])
or
puts sum_array [1, 2, 3, 4]

Divide array into subarray

I want to create two sub-arrays from this array:
a = [0, 1, 2, 3, 4, 5, 6]
This array will not always contain the same number of elements because it depends on the user input.
For example, in some occasions it'll be:
a = [0, 5]
or:
a = [5, 6, 4]
I want to divide the array into two subarrays. The first one will contain numbers from 1 to 4 (inclusive) and the second one will contain 0, 5 and 6.
In the first example, it will be:
a = [0, 1, 2, 3, 4, 5, 6]
sub_array1 = [1, 2, 3, 4]
sub_array2 = [0, 5, 6]
In the second:
a = [0, 5]
sub_array1 = []
sub_array2 = [5]
In the third:
a = [5, 6, 4]
sub_array1 = [4]
sub_array2 = [5, 6]
and so on, depending on the user input.
How can I do this?
First thing that comes to mind is Enumerable#partition.
sub_array1, sub_array2 = [0,1,2,3,4,5,6].partition {|x| (1..4).include? x }
=> [[1,2,3,4], [0,5,6]]
if you have two conditions (I mean if 0,5,6 are an actual condition and not the excluded set) I think that a double iteration wouldn't hurt
a = [0,1,2,3,4,5,6]
sub_array1 = a.select { |x| (1..4).include? x }
sub_array2 = a.select { |x| [0,5,6].include? x }
You can try something like this:
[0,1,2,3,4,5,6].group_by{|x| [0,5,6].include? x}
The result will be a hash:
{true=>[0, 5, 6], false=>[1, 2, 3, 4]}
In the second case:
[0,5].group_by{|x| [0,5,6].include? x}
The result will be:
{true=>[0, 5]}

Why doesn't this Python 3 for loop change every variable to 4?

list = [1,2,3,4,5]
print(list)
for each in list:
list[each] = 4
print(list)
And I get the result:
[1, 4, 3, 4, 4]
Line 4 seems to be setting "each" to 4 somehow...but I have no idea how. It's SUPPOSED to change the value at the current iterator to 4., which it also does at list[4].
Edit:
Wait wait wait, okay, 'each' is literally coming from the value inside the list? That's the only logical way this any sense, now that I think about it.
This will work better.
list = [1,2,3,4,5]
for each in range(len(list)):
list[each] = 4
print(list)
The problem you are running into is that your loop goes over the numbers from 1 - 5, but the index of the list starts at zero.
Adding a zero element to your list, or decrementing the each value by one in the loop makes your code work. But this way of doing it is flawed as you are depending on the content of the list to be in order and represent the positions.
list = [0,1,2,3,4,5] # zero added here.
for each in list:
list[each] = 4
print(list)
Your loop is actually doing this.
Loop 1: -> [1, 2, 3, 4, 5]
^
position 1 = 4.
Output: -> [1, 4, 3, 4, 5]
Loop 2: -> [1, 4, 3, 4, 5]
^
position 4 = 4.
Output: -> [1, 4, 3, 4, 4]
Loop 3: -> [1, 4, 3, 4, 4]
^
position 3 = 4. (it is already 4)
Output: -> [1, 4, 3, 4, 4]
Loop 4: -> [1, 4, 3, 4, 4]
^
position 4 = 4. (it is already 4)
Output: -> [1, 4, 3, 4, 4]
Loop 5: -> [1, 4, 3, 4, 4]
^
position 4 = 4. (it is already 4)
Output: -> [1, 4, 3, 4, 4]
Better use enumerate(), so you can skip the range(len()):
some_list = [1,2,3,4,5]
for i, item in enumerate(some_list):
some_list[i] = 4
print(some_list)
[4, 4, 4, 4, 4]
This will change each item in some_list to 4 by its index.
Why your way doesn't work
The thinking error you probably make it that the first item in a list has index 0, not 1 :)
"doing it your way" would then be:
some_list = [1,2,3,4,5]
print(some_list)
for each in some_list:
some_list[each-1] = 4
print(some_list)
[1, 2, 3, 4, 5]
[4, 4, 4, 4, 4]
EDIT
Another way to show what #JensB is explaining is to run the code below. It is exactly showing what happens in each of the iterations:
some_list = [1,2,3,4,5]
print(some_list)
for each in some_list:
some_list[each] = 4
print("some_list["+str(each)+"] = 4")
print(some_list)
[1, 2, 3, 4, 5]
some_list[1] = 4
[1, 4, 3, 4, 5]
some_list[4] = 4
[1, 4, 3, 4, 4]
some_list[3] = 4
[1, 4, 3, 4, 4]
some_list[4] = 4
[1, 4, 3, 4, 4]
some_list[4] = 4
[1, 4, 3, 4, 4]
When iterating over a list, you get the actual items, not the indexes (since the indexes are useless more often than not).
Actually, if you only need to iterate over the indexes, you could do it like this:
for i in range(len(your_list))
But to actually replace all items in the list with a single one, you could simply create a new one:
your_list = [4] * len(your_list)
Or if you prefer modifying the existing list:
your_list[:] = [4] * len(your_list)
Also, you should not name any variable list. This shadows the builtin list() function which is quite useful e.g. if you want to turn an iterable in a list (with list being shadowed you'd have to use [x for x in iterable] instead of list(iterable)).

Resources