Elixir: clone a list idiomatically - list

I can always do something like this:
new_list = Enum.map(old_list, fn x -> x end)
There is a dozen more equally, or marginally less ugly, ways of doing so, of course. Somehow, I can't find an idiomatic way of copying a list. There certainly must be a way.

Elixir is an immutable language, so the idiomatic way is this:
clone = original
There is no need to "clone". The data assigned to existing variables cannot be edited, so assigning one variable to another conceptually results in a copy of the data. You cannot edit existing data - if you reassign to an existing variable, you are conceptually pointing that variable at a new data-structure.
original = [1, 2, 3] |> IO.inspect(label: "original")
clone = original |> IO.inspect(label: "clone")
prepended = [0 | original] |> IO.inspect(label: "prepended")
original |> IO.inspect(label: "original again")
original = [5, 6, 7] |> IO.inspect(label: "original rebound")
clone |> IO.inspect(label: "clone again")
Output:
original: [1, 2, 3]
clone: [1, 2, 3]
prepended: [0, 1, 2, 3]
original again: [1, 2, 3]
original rebound: [5, 6, 7]
clone again: [1, 2, 3]

Since data structures in Elixir are immutable, I can't think of a reason you'd ever need to "clone" a list. It would do nothing. That being said, if you're looking for a way to do that exact nothing, you could reach for Enum.to_list/1.
iex> Enum.to_list([1, 2, 3])
[1, 2, 3]

Related

How can i sort a list from specifik criteria

I have this list and I want to sort the list. This is just a smaller example of what I want to do, but I get the same error. I dont understand why I can't make this work.
I have tried using google to solve the problem but without luck.
lst = [3, 4, 5, 6]
if lst < 4:
lst.pop()
print(lst)
How can i do this it shows
TypeError:'<' not supported between instances of 'list' and 'in
I think that your goal is to remove all elements in the list that are lesser than 4. You can use this simple list comprehension in order to achieve what you want:
lst = [3, 4, 5, 6]
lst = [elem for elem in lst if elem >= 4]
print(lst)
Output:
[4, 5, 6]

Math operations between values in list and list of lists (python3)

I'm stuck on what seems to be an easy problem :
I've got 2 lists of lists, let says :
a = [[1], [2]]
b = [[1, 2, 3], [4, 5, 6]]
And I want this result :
result = [[2, 3, 4], [6, 7, 8]]
by adding (or, why not, substracting ) a[0] value to each value of b[0], then a[1] to b[1] etc...
I've tried using zip without result as expected:
result = [x for x in zip(a, b)]
Can someone help me to progress ?
you have a list of lists with 1 element, and you want to apply addition of that element on all elements of the other list. Since expected result is a list of lists, you have to create a double list comprehension, like this:
a = [[1], [2]]
b = [[1, 2, 3], [4, 5, 6]]
result = [[x+v for x in l] for [v],l in zip(a,b)]
print(result)
result:
[[2, 3, 4], [6, 7, 8]]
for [v],l is a neat way of unpacking the element inside the list so it avoids x+v[0] in the loop and it's more performant (and pythonic). Plus: if the list suddenly contains more than 1 element, you'll get an unpack error instead of an unexpected result (by ignoring further elements).
You can do this using numpy, which inherently supports array operations such as this:
>>> import numpy as np
>>> i = np.array([[1], [2]])
>>> j = np.array([[1, 2, 3], [4, 5, 6]])
>>> i+j
array([[2, 3, 4],
[6, 7, 8]])
If your lists are large, this may have a speed advantage over list comprehensions due to the fact that numpy uses fast low-level routines for this sort of stuff.
If not, and you don't already have numpy installed, then the overhead of installing another library is probably not worth it.

python3.2)append two element in a list(lists in a list)

If I have an input like this (1, 2, 3, 4, 5, 6)
The output has to be ... [[1, 2], [3, 4], [5, 6]].
I know how to deal with if it's one element but not two.
x=[]
for number in numbers:
x.append([number])
I'll appreciate your any help!
Something like this would work:
out = []
lst = (1,2,3,4,5,6,7,8,9,10)
for x in range(len(lst)):
if x % 2 == 0:
out.append([lst[x], lst[x+1]])
else:
continue
To use this, just set lst equal to whatever list of numbers you want. The final product is stored in out.
There is a shorter way of doing what you want:
result = []
L = (1,2,3,4,5,6,7,8,9,10)
result = [[L[i], L[i + 1]] for i in range(0, len(L) - 1, 2)]
print(result)
You can use something like this. This solution also works for list of odd length
def func(lst):
res = []
# Go through every 2nd value | 0, 2, 4, ...
for i in range(0, len(lst), 2):
# Append a slice of the list, + 2 to include the next value
res.append(lst[i : i + 2])
return res
# Output
>>> lst = [1, 2, 3, 4, 5, 6]
>>> func(lst)
[[1, 2], [3, 4], [5, 6]]
>>> lst2 = [1, 2, 3, 4, 5, 6, 7]
>>> func(lst2)
[[1, 2], [3, 4], [5, 6], [7]]
List comprehension solution
def func(lst):
return [lst[i:i+2] for i in range(0, len(lst), 2)]
Slicing is better in this case as you don't have to account for IndexError allowing it to work for odd length as well.
If you want you can also add another parameter to let you specify the desired number of inner elements.
def func(lst, size = 2): # default of 2 it none specified
return [lst[i:i+size] for i in range(0, len(lst), size)]
There's a few hurdles in this problem. You want to iterate through the list without going past the end of the list and you need to deal with the case that list has an odd length. Here's one solution that works:
def foo(lst):
result = [[x,y] for [x,y] in zip(lst[0::2], lst[1::2])]
return result
In case this seems convoluted, let's break the code down.
Index slicing:
lst[0::2] iterates through lst by starting at the 0th element and proceeds in increments of 2. Similarly lst[1::2] iterates through starting at the 1st element (colloquially the second element) and continues in increments of 2.
Example:
>>> lst = (1,2,3,4,5,6,7)
>>> print(lst[0::2])
(1,3,5,7)
>>> print(lst[1::2])
(2,4,6)
zip: zip() takes two lists (or any iterable object for that matter) and returns a list containing tuples. Example:
>>> lst1 = (10,20,30, 40)
>>> lst2 = (15,25,35)
>>> prit(zip(lst1, lst2))
[(10,15), (20,25), (30,35)]
Notice that zip(lst1, lst2) has the nice property that if one of it's arguments is longer than the other, zip() stops zipping whenever the shortest iterable is out of items.
List comprehension: python allows iteration quite generally. Consider the statement:
>>> [[x,y] for [x,y] in zip(lst1,lst2)]
The interior bit "for [x,y] in zip(lst1,lst2)" says "iterate through all pairs of values in zip, and give their values to x and y". In the rest of the statement
"[[x,y] for [x,y] ...]", it says "for each set of values x and y takes on, make a list [x,y] to be stored in a larger list". Once this statement executes, you have a list of lists, where the interior lists are all possible pairs for zip(lst1,lst2)
Very Clear solution:
l = (1, 2, 3, 4, 5, 6)
l = iter(l)
w = []
for i in l:
sub = []
sub.append(i)
sub.append(next(l))
w.append(sub)
print w

Prolog how do I append a list of lists into one list?

So I'm totally new to Prolog and need some help. I'm trying to take a list of lists like [[1,2,3],[4,5,6],[7,8]] and create a list like [2,3,5,6,8], so basically all the values into a new list besides the first of each list. I got this:
test5(X,[[_|X]|_]).
test5(X,[_|A]) :- test5(X,A).
which returns [2,3] and then [5,6] and then [8] each time I press enter. I'm not sure how to make them run all at once and make them into a list. I tried using append in different ways but I could not get this working. Any idea on how to implement this? Thanks!
You have the common predicate flatten/2, which almost does the job:
?- flatten([[1,2,3],[4,5,6],[7,8]], L).
L = [1, 2, 3, 4, 5, 6, 7, 8].
There are many implementations of flatten/2 available, just google it.
If you know that the list of lists is not nested, you should rather use append/2.
Then, you need to drop the first element of each list before appending:
list_tail([_|T], T).
Then:
?- maplist(list_tail, [[1,2,3],[4,5,6],[7,8]], T), append(T, L).
T = [[2, 3], [5, 6], [8]],
L = [2, 3, 5, 6, 8].
It might be a good exercise to take a more careful look at the implementation of append/2 linked above. With a small change in the definition (literally removing 1 character and adding 5) it will do the dropping and appending in the same step, without traversing the original list twice.
EDIT
So why is it that #repeat's initial solution does not terminate when the first argument is not a proper list, but the second is a proper list?
nt_tails_append([[_|T]|Ls], As) :-
append(T, Ws, As),
nt_tails_append(Ls, Ws).
It is because when the first argument to nt_tails_append/2 is a free variable, the first two arguments to append/3 above are variables, too. When we call append/3 in this mode, we get, by definition:
?- append(A, B, L).
A = [],
B = L .
In other words, the second and the third arguments are now unified. With the definition of nt_tail_append/2, this means that the recursive call gets the same second argument as the original call, and a new free variable as the first argument. This is an endless loop, of course.
(Tellingly, if you care to look at the definition of append/2 linked above, you will see that the first argument must_be a list.)
How does this help?
tails_append(Ls, As) :-
maplist(list_tail, Ls, T),
append(T, As).
list_tail([_|T], T).
The way that maplist is defined, all list arguments will be instantiated to proper lists. So you can safely use append/3 (here, used in the definition of append/2).
Here is how you could do it using append/3:
lists_concatenatedTails([],[]).
lists_concatenatedTails([[_|Xs0]|Xss],Ys) :-
append(Xs0,Ys0,Ys),
lists_concatenatedTails(Xss,Ys0).
Sample query:
?- lists_concatenatedTails([[1,2,3],[4,5,6],[7,8]], Xs).
Xs = [2, 3, 5, 6, 8].
Edit 2015-05-07
Note that the code that #Boris suggested (using list_tail/2,maplist/3,append/2) also gives answers for the following query:
?- maplist(list_tail,Xss,Yss), append(Yss,[1,2,3]).
Xss = [[_G97, 1, 2, 3]], Yss = [[1, 2, 3]] ;
Xss = [[_G97], [_G106, 1, 2, 3]], Yss = [[], [1, 2, 3]] ;
Xss = [[_G97, 1], [_G106, 2, 3]], Yss = [[1], [2, 3]] ;
Xss = [[_G97, 1, 2], [_G106, 3]], Yss = [[1, 2], [3]] ;
Xss = [[_G97, 1, 2, 3], [_G106]], Yss = [[1, 2, 3], []] ;
Xss = [[_G97], [_G106], [_G115, 1, 2, 3]], Yss = [[], [], [1, 2, 3]] ...
This doesn't terminate universally---nor do we expect it to: the set of solutions is infinite in size and it can, in this case, only be covered by an infinite sequence of answers.
In the following equivalent query lists_concatenatedTails/2 "loops" right away:
?- lists_concatenatedTails(Lss,[1,2,3]).
% not a single answer within finite time
Only when constraining the length of Lss right away, fair enumeration can be achieved:
?- length(Lss,_), lists_concatenatedTails(Lss,[1,2,3]).
Lss = [[_G23, 1, 2, 3]] ;
Lss = [[_G26], [_G29, 1, 2, 3]] ;
Lss = [[_G26, 1], [_G32, 2, 3]] ;
Lss = [[_G26, 1, 2], [_G35, 3]] ;
Lss = [[_G26, 1, 2, 3], [_G38]] ;
Lss = [[_G29], [_G32], [_G35, 1, 2, 3]] ...

Modifying copy of list, modifies original

I thought I understood how Python handles copies of mutables, but I came upon the following behavior.
x = [i for i in xrange(5)]
mylist = [x, x]
mylist_copy = mylist[:]
mylist_copy[0].pop()
mylist
Out : [[0, 1, 2, 3], [0, 1, 2, 3]]
So it seems mylist was changed despite changes being made only to mylist_copy. How can I create a pure copy of mylist so that this does not occur?
While you're indeed making a copy, it is a shallow copy: mylist_copy contains references to the original x.
To make a deep copy, use copy.deepcopy().
This happens not because of any copying errors when making mylist_copy, but because mylist is made up of two of the same elements.
mylist = [x,x] adds two references to the same object (`x`).
As a result, mylist[0].pop() will have the same outcome:
In [70]: x = [i for i in range(5)]
In [71]: mylist = [x,x]
In [72]: mylist
Out[72]: [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]
In [73]: mylist[0].pop()
Out[73]: 4
In [74]: mylist
Out[74]: [[0, 1, 2, 3], [0, 1, 2, 3]]
Here's one way to circumvent this issue:
In [80]: x = [i for i in range(5)]
In [81]: mylist = [x, x[:]]
In [82]: mylist_copy = mylist[:]
In [83]: mylist_copy[0].pop()
Out[83]: 4
In [84]: mylist
Out[84]: [[0, 1, 2, 3], [0, 1, 2, 3, 4]]