I tried to create a problem like [0,1,2,[0,1,2,[0,1,2,[0,1,2] ] ]... by using recursion it but it got some bugs that I don't know where it came from.
It creates an output like this:
[1, 2, 3, 4, 5, [...]]
So let's make an example x = [1,2,3,4,5] and len_ = len(x) for the recursion. The code will be like this:
list_ = [1,2,3,4,5]
n = len(list_)
dull = []
def BlastedField3(n):
global list_
list_ += [list_]
if n == 1:
return list_
if n > 1:
return list_
return BlastedFild(n-1)
print(BlastedField3(n))
So I was imagining that list_ += [list_] was something like per each recursion of the function the list_ += [list_] working too.But! it works but the bug [...] is coming up next to it.
Is there a way to solve this without this bug [...] or just try a loop?
The problem of creating a self-containing list can be solved by not using references but shallow copies and by not reusing global variables (whenever you feel the need for those, look at your design long and hard).
base = [1,2,3,4,5]
def blasted(n):
if n <= 1:
return base[:] # shallow copy to never accidentally expose your base
return base + [blasted(n-1)]
>>> blasted(1)
[1, 2, 3, 4, 5]
>>> blasted(2)
[1, 2, 3, 4, 5, [1, 2, 3, 4, 5]]
>>> blasted(3)
[1, 2, 3, 4, 5, [1, 2, 3, 4, 5, [1, 2, 3, 4, 5]]]
Actually the use of shallow copies is not strictly necessary as the code never mutates the base list. But I would still recommend it because if someone were to mutate any result of the blast, base might be affected.
Related
This question already has answers here:
Sort a part of a list in place
(3 answers)
Closed 3 years ago.
Suppose I have a list [2, 4, 1, 3, 5].
I want to sort the list just from index 1 to the end, which gives me [2, 1, 3, 4, 5]
How can I do it in Python?
(No extra spaces would be appreciated)
TL;DR:
Use sorted with a slicing assignment to keep the original list object without creating a new one:
l = [2, 4, 1, 3, 5]
l[1:] = sorted(l[1:])
print(l)
Output:
[2, 1, 3, 4, 5]
Longer Answer:
After the list is created, we will make a slicing assignment:
l[1:] =
Now you might be wondering what does [1:], it is slicing the list and starts from the second index, so the first index will be dropped. Python's indexing starts from zero, : means get everything after the index before, but if it was [1:3] it will only get values that are in between the indexes 1 and 3, let's say your list is:
l = [1, 2, 3, 4, 5]
If you use:
print(l[1:])
It will result in:
[2, 3, 4, 5]
And if you use:
print(l[1:3])
It will result in:
[2, 3]
About slicing, read more here if you want to.
And after slicing we have an equal sign =, that just simply changes what's before the = sign to what's after the = sign, so in this case, we use l[1:], and that gives [2, 3, 4, 5], it will change that to whatever is after the = sign.
If you use:
l[1:] = [100, 200, 300, 400]
print(l)
It will result in:
[1, 100, 200, 300, 400]
To learn more about it check out this.
After that, we got sorted, which is default builtin function, it simple sorts the list from small to big, let's say we have the below list:
l = [3, 2, 1, 4]
If you use:
print(sorted(l))
It will result in:
[1, 2, 3, 4]
To learn more about it check this.
After that we come back to our first topic about slicing, with l[1:], but from here you know that it isn't only used for assignments, you can apply functions to it and deal with it, like here we use sorted.
Maybe temporarily put something there that's smaller than the rest? Should be faster than the other solutions. And gets as close to your "No extra spaces" wish as you can get when using sort or sorted.
>>> tmp = l[0]
>>> l[0] = float('-inf')
>>> l.sort()
>>> l[0] = tmp
>>> l
[2, 1, 3, 4, 5]
Benchmarks
For the example list, 1,000,000 iterations (and mine of course preparing that special value only once):
sort_u10 0.8149 seconds
sort_chris 0.8569 seconds
sort_heap 0.7550 seconds
sort_heap2 0.5982 seconds # using -1 instead of -inf
For 50,000 lists like [int(x) for x in os.urandom(100)]:
sort_u10 0.4778 seconds
sort_chris 0.4786 seconds
sort_heap 0.8106 seconds
sort_heap2 0.4437 seconds # using -1 instead of -inf
Benchmark code:
import timeit, os
def sort_u10(l):
l[1:] = sorted(l[1:])
def sort_chris(l):
l = l[:1] + sorted(l[1:])
def sort_heap(l, smallest=float('-inf')):
tmp = l[0]
l[0] = smallest
l.sort()
l[0] = tmp
def sort_heap2(l):
tmp = l[0]
l[0] = -1
l.sort()
l[0] = tmp
for _ in range(3):
for sort in sort_u10, sort_chris, sort_heap, sort_heap2, sort_rev:
number, repeat = 1_000_000, 5
data = iter([[2, 4, 1, 3, 5] for _ in range(number * repeat)])
# number, repeat = 50_000, 5
# data = iter([[int(x) for x in os.urandom(100)] for _ in range(number * repeat)])
t = timeit.repeat(lambda: sort(next(data)), number=number, repeat=repeat)
print('%10s %.4f seconds' % (sort.__name__, min(t)))
print()
Use sorted with slicing:
l[:1] + sorted(l[1:])
Output:
[2, 1, 3, 4, 5]
For the special case that you actually have, according to our comments:
Q: I'm curious: Why do you want this? – Heap Overflow
A: I'm trying to make a next_permutation() in python – nwice13
Q: Do you really need to sort for that, though? Not just reverse? – Heap Overflow
A: Yup, reverse is ok, but I just curious to ask about sorting this way. – nwice13
I'd do that like this:
l[1:] = l[:0:-1]
You can define your own function in python using slicing and sorted and this function (your custom function) should take start and end index of the list.
Since list is mutable in python, I have written the function in such a way it doesn't modify the list passed. Feel free to modify the function. You can modify the list passed to this function to save memory if required.
def sortedList(li, start=0, end=None):
if end is None:
end = len(li)
fi = []
fi[:start] = li[:start]
fi[start:end] = sorted(li[start:end])
return fi
li = [2, 1, 4, 3, 0]
print(li)
print(sortedList(li, 1))
Output:
[2, 1, 4, 3, 0]
[2, 0, 1, 3, 4]
The general project is to re-build an enigma machine. However, the problem is this:
I have a list and I want to rearrange it in a way that the first element is appended to the same list and then the first element is deleted.
Basicly, I want this:
l = [1, 2, 3, 4]
l_new = [2, 3, 4, 1]
for this purpose I constructed the function
def switch_up(list):
list.append(list[0])
del list[0]
return list
The problem: When calling this function with a list, it does not only return the changed list, but changes the original list given as the argument.
The full code looks like this:
def switch_up(list):
list.append(list[0])
del list[0]
return list
my_list = [1, 2, 3, 4]
my_list2 = switch_up(my_list)
print(my_list)
print(my_list2)
My expected/desired output would be:
[1, 2, 3, 4]
[2, 3, 4, 1]
The output I get is:
[2, 3, 4, 1]
[2, 3, 4, 1]
You are altering the list passed into the function. That isn't a copy of the list it's the same list. You should just make a copy and return it with something like:
def switch_up(l):
# list slices will make shallow copy
return l[1:] + l[:1]
my_list = [1, 2, 3, 4]
my_list2 = switch_up(my_list)
print(my_list)
# [1, 2, 3, 4]
print(my_list2)
# [2, 3, 4, 1]
Lists are mutable in python and therefore it is assumed, that you want to do operations on the original list. If you do not want that you have to manually copy your list for example with list.copy. For more information on copy please see this stackoverflow question
All you created is a "pointer" called my_list2 to the same value as my_list if you would for example iclude my_list2[1] = 'a' the result would be:
my_list = [2, 'a', 4, 1]
my_list2 = [2, 'a', 4, 1]
And btw you should never name your variables the same as inbuilt functions, like list
The following reverses a list "in-place" and works in Python 2 and 3:
>>> mylist = [1, 2, 3, 4, 5]
>>> mylist[:] = reversed(mylist)
>>> mylist
[5, 4, 3, 2, 1]
Why/how? Since reversed gives me an iterator and doesn't copy the list beforehand, and since [:]= replaces "in-place", I am surprised. And the following, also using reversed, breaks as expected:
>>> mylist = [1, 2, 3, 4, 5]
>>> for i, item in enumerate(reversed(mylist)):
mylist[i] = item
>>> mylist
[5, 4, 3, 4, 5]
Why doesn't the [:] = fail like that?
And yes, I do know mylist.reverse().
CPython list slice assigment will convert the iterable to a list first by calling PySequence_Fast. Source: https://hg.python.org/cpython/file/7556df35b913/Objects/listobject.c#l611
v_as_SF = PySequence_Fast(v, "can only assign an iterable");
Even PyPy does something similar:
def setslice__List_ANY_ANY_ANY(space, w_list, w_start, w_stop, w_iterable):
length = w_list.length()
start, stop = normalize_simple_slice(space, length, w_start, w_stop)
sequence_w = space.listview(w_iterable)
w_other = W_ListObject(space, sequence_w)
w_list.setslice(start, 1, stop-start, w_other)
Here space.listview will call ObjSpace.unpackiterable to unpack the iterable which in turn returns a list.
Input list: [1, 2, 3, 4, 5]
Output: [5, 4, 3, 2, 1]
I know how to do it with for loop, but my assignment is to do it with while loop; which I have no idea to do. Here is the code I have so far:
def while_version(items):
a = 0
b = len(items)
r_list = []
while (a!=b):
items[a:a] = r_list[(-a)-1]
a+=1
return items
I would say to make the while loop act like a for loop.
firstList = [1,2,3]
secondList=[]
counter = len(firstList)-1
while counter >= 0:
secondList.append(firstList[counter])
counter -= 1
The simplest way would be:
def while_version(items):
new_list = []
while items: # i.e. until it's an empty list
new_list.append(items.pop(-1))
return new_list
This will reverse the list:
>>> l1 = [1, 2, 3]
>>> l2 = while_version(l)
>>> l2
[3, 2, 1]
Note, however, that it also empties the original list:
>>> l1
[]
To avoid this, call e.g. l2 = while_version(l1[:]).
The trivial answer
Given
a = [1, 2, 3, 4, 5]
then
a[::-1]
returns
[5, 4, 3, 2, 1]
In your code:
You use r_list[(-a)+1], buy you have never assigned r_list any value (just "r_list = []")
I think your are confusing "items" with "r_list". So I think you want to return "r_list" instead of "items" (the input parameter)
The assignment should be "r_list[a] = items[-a-1]", but that doesn't work. You should use "r_list.append(items[-a-1])"
The return should be "return r_list"
"while (a!=b)" should be "while (a < b)" for readeability
Hope this helps
Basically, I have a list and I want to perform multiple functions on it at once. For example,
List = [1,2,3,4,5]
List.extend([1,2,3,4,5]).sort().reverse()
I would like the result to be [5,5,4,4,3,3,2,2,1,1].
I haven't used Python in a while, but I know I've done something like this before. Is it something simple I'm missing or what?
It has to all be on one line by the way.
Most Python methods that mutate a container in-place return None
However your example can easily be handled in one line
L = [1,2,3,4,5]
L = sorted(L+[1,2,3,4,5], reverse=True)
Keeping in the spirit of the challenge, it's not hard to chain the operations (because they always return None)
>>> L = [1, 2, 3, 4, 5]
>>> L.extend([1,2,3,4,5]) or L.sort() or L.reverse() or L
[5, 5, 4, 4, 3, 3, 2, 2, 1, 1]
Here's another way
>>> L = [1, 2, 3, 4, 5]
>>> (L.extend([1,2,3,4,5]), L.sort(), L.reverse()) and L
[5, 5, 4, 4, 3, 3, 2, 2, 1, 1]
And you can let your imagination run wild
>>> L = [1, 2, 3, 4, 5]
>>> max(L.extend([1,2,3,4,5]), L.sort(), L.reverse(), L) # Python2.x only
[5, 5, 4, 4, 3, 3, 2, 2, 1, 1]
You cannot chain the above operations since they are performed in-place.
As alternatives, use sorted() and reversed().
How about:
>>> l = [1,2,3,4,5]*2
>>> l.sort(reverse=True)
>>> l
[5, 5, 4, 4, 3, 3, 2, 2, 1, 1]
Or even shorter:
>>> sorted([1,2,3,4,5]*2,reverse=True)
[5, 5, 4, 4, 3, 3, 2, 2, 1, 1]
Each of those functions acts on the list in-place, returning None and not the modified list. You'd have to do something like
l = [1,2,3,4,5]
l = reversed(sorted(l + [1,2,3,4,5]))
Not perfect but interesting..
class chain():
def __init__(self, my_object):
self.o = my_object
def __getattr__(self, attr):
x = getattr(self.o, attr)
if hasattr(x, '__call__'):
method = x
return lambda *args: self if method(*args) is None else method(*args)
else:
prop = x
return prop
list_ = chain([1, 2, 3, 0])
print list_.extend([9,5]).sort().reverse()
The functions extend, sort and reverse doesn't return list but change the list on which they are called. Chaining relies on a fact that previous function returns some value and works on it.
E.g. s="abc" then you can do s.upper().lower() as .upper() return a new string in uppercase (ans same for .lower()).