I have some strange behavior on python 3.7 with a nested list comprehension that involves a generator.
This works:
i = range(20)
n = [1, 2, 3]
result = [min(x + y for x in i) for y in n]
It does not work if i is a generator:
i = (p for p in range(20))
n = [1, 2, 3]
result = [min(x + y for x in i) for y in n]
This raises a ValueError: min() arg is an empty sequence
Now even if the generator i is wrapped with list it still creates the same error:
i = (p for p in range(20))
n = [1, 2, 3]
result = [min(x + y for x in list(i)) for y in n]
Is this a python bug or is it expected behavior? If it is expected behavior, can you explain why this does not work?
In i = range(20) the range(20) is a promise to generate a generator.
While i = (p for p in range(20)) is already a generator.
Now write your list expression as:
for y in [1, 2, 3]:
print(min(x + y for x in i))
## 1
## ...
## ValueError: min() arg is an empty sequence
You get a 1 printed, but (the generator is exhausted in the first call)
and then you get in the next round a
ValueError: min() arg is an empty sequence
because the generator i was already consumed in the first for-loop call for y as 1.
While if i is defined as range(20),
everytime the for x in i is called, the generator is re-created again and again.
You can imitate what range(20) is doing by:
def gen():
return (p for p in range(20))
for y in [1, 2, 3]:
print(min(x + y for x in gen()))
# range() like gen() is a promise to generate the generator
## 1
## 2
## 3
Now the generator is created everytime anew.
But in fact, range is even cooler, if you do:
i = range(20)
for y in [1, 2, 3]:
print(min(x + y for x in i))
## 1
## 2
## 3
The i inside the innerst generator is not a function call.
But despite of that it creates - when evaluted - a new generator -
at least when used as an iterable within a for loop.
This is actually implemented in Python using a class and by defining the __iter__() method. Which defines the behaviour in interators - here especiall a lazy behavior.
To imitate this behavior, we can generate a lazy generator (lazy_gen).
class lazy_gen:
def __init__(self):
pass
def __iter__(self): # everytime when used as an iterator
return self.gen() # recreate the generator # real lazy behavior
def gen(self):
return (p for p in range(20))
Which we can use like:
i = lazy_gen()
for y in [1, 2, 3]:
print(min(x + y for x in i))
## 1
## 2
## 3
So this reflects even better the range() behavior.
Other languages (functional languages) like Lisp family languages (common-lisp, Racket, Scheme, Clojure), R, or Haskell
have a better control over evaluation - thus over lazy evaluation and promises. But in Python, for such implementations and fine grained control, one has to take resort in OOP.
My range function and class
Finally, I figured out how the range function must have been realized roughly.
(For fun, though I could have looked it up in the source code of Python I know - but sometimes reasoning is fun.)
class Myrange:
def __init__(self, start, end, step):
self.start = start
self.end = end
self.step = step
def __iter__(self):
return self.generate_range()
def generate_range(self):
x = self.start - self.step
while x + self.step < self.end:
x = x + self.step
yield x
def __repr__(self):
return "myrange({}, {})".format(self.start, self.end)
def myrange(start=None, end=None, step=1):
if start is None and end is None:
raise "Please provide at least one number for the range-limits."
elif start is not None and end is None:
_start = 0
_end = start
elif start is not None and end is not None:
_start = start
_end = end
else:
_start = 0
_end = end
_step = step
return Myrange(_start, _end, _step)
One can use it exactly like the range function.
i = myrange(20)
n = [1, 2, 3]
result = [min(x + y for x in i) for y in n]
result
## [1, 2, 3]
i
## myrange(0, 20) # representation of a Myrange object.
myrange(20)
## myrange(0, 20)
list(myrange(3, 10))
## [3, 4, 5, 6, 7, 8, 9]
list(myrange(0, 10))
## [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list(myrange(10))
## [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
list(myrange(0, 10, 2))
## [0, 2, 4, 6, 8]
list(myrange(3, 10, 2))
## [3, 5, 7, 9]
In both of your last examples, you try to iterate on the generator again after it got exhausted.
In your last example, list(i) is evaluated again for each value of y, so i will be exhausted after the first run.
You have to make a list of the values it yields once before, as in:
i = (p for p in range(20))
n = [1, 2, 3]
list_i = list(i)
result = [min(x + y for x in list_i) for y in n]
The generator is emptied after the first for loop for both for x in i or for x in list(i), instead you need to convert the generator to a list, (which essentially iterates over the generator and empties it) beforehand and use that list
Note that this essentially defeats the purpose of a generator, since now this becomes the same as the first approach
In [14]: list(range(20)) == list(p for p in range(20))
Out[14]: True
Hence the updated code will be
#Create generator and convert to list
i = list(p for p in range(20))
n = [1, 2, 3]
#Use that list in the list comprehension
result = [min(x + y for x in i) for y in n]
print(result)
The output will be
[1, 2, 3]
Hence the better approach hence is to stick with the first approach itself, or you can have the generator inline, which, again is the same as the first approach with range
n = [1, 2, 3]
result = [min(x + y for x in (p for p in range(20))) for y in n]
print(result)
#[1, 2, 3]
Related
I have this list:
arr = [1, 2, 3, 4, 5, 6]
What I wanted to do is make a new list with values from index 5 to index 1.
Output would be:
[6, 1, 2]
This is what I've done:
output = arr[5:] + arr[:2]
But I wanted to know if there's another way of slicing it.
Like just a normal slicing like for example:
output = arr[5:1]
But I know it's not gonna work because I've done that. Could you please help me?
As far as I'm aware, doing this without writing your own custom code doesn't seem possible. Python doesn't wrap lists around.
You can create a custom generator to do what you want, though:
>>> def cycle(l, start=0):
... l = l[start:] + l[:start]
... while True:
... x = l.pop(0)
... yield x
... l.append(x)
...
>>> k = cycle(a, 5)
>>> next(k)
6
>>> next(k)
1
>>> next(k)
2
(Example rolled back due to OP's post change.)
Here's an improved version that will take into account the number elements you want to get from the generator:
>>> def cycle(l, start=0, iters=None):
... l = l[start:] + l[:start]
... i = 0
... while True:
... if iters is not None and i == iters:
... raise StopIteration
... x = l.pop(0)
... yield x
... l.append(x)
... i += 1
...
>>> a = [1, 2, 3, 4, 5, 6]
>>> list(cycle(a, start=5, iters=3))
[6, 1, 2]
Update:
Rotate left n elements (or right for negative n) and slice number of element you want
L = L[n:] + L[:n] # rotate left n elements
In ur case n is 5:
>>> output = arr[5:] + arr[:5]
>>> output[:3]
[6, 1, 2]
Previous
>>> arr = [1, 2, 3, 4, 5, 6]
>>> output = arr[:]
>>> del output[2:5]
>>> output
[1, 2, 6]
>>>
Create a function to slice the input array for you and append the two parts together to get the desired list.
def cyc_slice(lst, start, n):
return lst[start:] + lst[:(start+n)%len(lst)]
Unlike both other answers, this doesn't make a superflous copy of all the list elements that you don't want.
>>> arr=[1,2,3,4,5,6]
>>> cyc_slice(arr, 5, 3)
[6, 1, 2]
And an improved iterator solution:
def cycle(l, start=0, n=None):
if not l:
return
idx = start-1
end = (idx + n) % len(l) if n else -1
while idx != end:
idx+=1
try:
x = l[idx]
except IndexError:
idx = 0
x = l[idx]
yield x
when provided with a count, it will provide that many elements. Otherwise, it can keep looping. This iterates through the list in place, so doesn't allocate any elements to memory (unless you create a list from it)
>>> list(cycle(arr,5,3))
[6, 1, 2]
This question already has answers here:
Efficient way to rotate a list in python
(27 answers)
Closed last month.
I'd like to rotate a Python list by an arbitrary number of items to the right or left (the latter using a negative argument).
Something like this:
>>> l = [1,2,3,4]
>>> l.rotate(0)
[1,2,3,4]
>>> l.rotate(1)
[4,1,2,3]
>>> l.rotate(-1)
[2,3,4,1]
>>> l.rotate(4)
[1,2,3,4]
How might this be done?
def rotate(l, n):
return l[-n:] + l[:-n]
More conventional direction:
def rotate(l, n):
return l[n:] + l[:n]
Example:
example_list = [1, 2, 3, 4, 5]
rotate(example_list, 2)
# [3, 4, 5, 1, 2]
The arguments to rotate are a list and an integer denoting the shift. The function creates two new lists using slicing and returns the concatenatenation of these lists. The rotate function does not modify the input list.
If applicable, you could use collections.deque as a solution:
import collections
d = collections.deque([1,2,3,4,5])
d.rotate(3)
print d
>>> deque([3, 4, 5, 1, 2])
As a bonus, I'd expect it to be faster than in-built list.
The following function will rotate the list l, x spaces to the right:
def rotate(l, x):
return l[-x:] + l[:-x]
Note that this will only return the original list if x is outside the range [-len(l), len(l)]. To make it work for all values of x, use:
def rotate(li, x):
return li[-x % len(li):] + li[:-x % len(li)]
>>> l=[1,2,3,4]
>>> l[1:]+l[:1]
[2, 3, 4, 1]
>>> l=[1,2,3,4]
>>> l[2:]+l[:2]
[3, 4, 1, 2]
>>> l[-1:]+l[:-1]
[4, 1, 2, 3]
A general rotate n to the left (positive y in the call to rotate) or right (negative y) then:
def rotate(l, y=1):
if len(l) == 0:
return l
y = y % len(l) # Why? this works for negative y
return l[y:] + l[:y]
If you want the direction of rotation to be the same as your example, just negate y in rotate.
def rotate(l, y=1):
if len(l) == 0:
return l
y = -y % len(l) # flip rotation direction
return l[y:] + l[:y]
I understand this question has been asked before but I haven't seen any that answer it in a way without splitting the list.
say I have a list:
num = [1,2,3,4,5,6]
I want to create a function:
rotate(lst, x):
So that if I call rotate(num, 3) it will globally edit the list num. That way when I later call print(num) it will result in [4,5,6,1,2,3].
I understand that I could write the function something like:
rotate(lst, x):
return [lst[-x:] + lst[:-x]
But I need to do this function without a return statement, and without splitting the list. What I'm thinking would work would be to put the last value of the list into a variable: q = lst[-1] and then from there create a loop that runs x amount of times that continues to move the values towards the end of the list and replacing the 0th position with whatever is stored in q.
One more thing. If I call rotate(lst, -3) then instead of rotating to the "right" it would have to rotate to the "left".
I'm new to python and having trouble wrapping my mind around this concept of manipulating lists. Thank you everyone for your time and effort. I hope this problem was clear enough.
You can use slicing assignment to modify your current strategy to do what you want. You're already generating the rotated list correctly, just modify the list in place with lst[:] = ...
def rotate(lst, x):
lst[:] = lst[-x:] + lst[:-x]
Example in the interactive interpreter:
>>> l = [1, 2, 3, 4, 5, 6]
>>> def rotate(lst, x):
... lst[:] = lst[-x:] + lst[:-x]
...
>>> rotate(l, 2)
>>> l
[5, 6, 1, 2, 3, 4]
Now rotate it backwards:
>>> rotate(l, -2)
>>> l
[1, 2, 3, 4, 5, 6]
>>> rotate(l, -2)
>>> l
[3, 4, 5, 6, 1, 2]
See this answer on a different question: https://stackoverflow.com/a/10623383/3022310
Here is a solution using a double-ended queue.
As required, it modifies the list in place, neither uses return nor uses chunks of the list.
from collections import deque
def rotate(lst, x):
d = deque(lst)
d.rotate(x)
lst[:] = d
num = [1,2,3,4,5,6]
rotate(num,3)
print(num)
rotate(num,-3)
print(num)
produces
[4, 5, 6, 1, 2, 3]
[1, 2, 3, 4, 5, 6]
Please have a look at PMOTW's tutorial on deque
def rotate(lst, num):
copy = list(lst)
for (i, val) in enumerate(lst):
lst[i] = copy[i - num]
Try:
num = [1,2,3,4,5,6]
def rotate(lst,x):
copy = list(lst)
for i in range(len(lst)):
if x<0:
lst[i+x] = copy[i]
else:
lst[i] = copy[i-x]
rotate(num, 2)
print num
Here is a simple method using pop and insert on the list.
num = [1,2,3,4,5,6]
def rotate(lst, x):
if x >= 0:
for i in range(x):
lastNum = lst.pop(-1)
lst.insert(0, lastNum)
else:
for i in range(abs(x)):
firstNum = lst.pop(0)
lst.append(firstNum)
return
print num #[1, 2, 3, 4, 5, 6]
rotate(num, 2)
print num #[5, 6, 1, 2, 3, 4]
rotate(num, -2)
print num #[1, 2, 3, 4, 5, 6]
I believe this satisfies all requirements. The idea is from the Programming Pearls book(http://goo.gl/48yJPw). To rotate a list we can reverse it and then reverse sublists with the rotating index as pivot.
def rotate(num, rot):
if rot < 0:
rot = len(num) + rot
rot = rot - 1
num.reverse()
for i in range(rot/2 + 1):
num[i], num[rot-i] = num[rot-i], num[i]
for i in range(1, (len(num) - rot)/2):
num[rot+ i], num[len(num) - i] = num[len(num) - i], num[rot+ i]
#Testing...
num = range(1, 10)
rot = -1
print num
rotate(num, rot)
print num
So I am implementing a block swap algorithm in python.
The algorithm I am following is this:
Initialize A = arr[0..d-1] and B = arr[d..n-1]
1) Do following until size of A is equal to size of B
a) If A is shorter, divide B into Bl and Br such that Br is of same
length as A. Swap A and Br to change ABlBr into BrBlA. Now A
is at its final place, so recur on pieces of B.
b) If A is longer, divide A into Al and Ar such that Al is of same
length as B Swap Al and B to change AlArB into BArAl. Now B
is at its final place, so recur on pieces of A.
2) Finally when A and B are of equal size, block swap them.
The same algorithm has been implemented in C on this website - Array Rotation
My python code for the same is
a = [1,2,3,4,5,6,7,8]
x = 2
n = len(a)
def rotate(a,x):
n = len(a)
if x == 0 or x == n:
return a
if x == n -x:
print(a)
for i in range(x):
a[i], a[(i-x+n) % n] = a[(i-x+n) % n], a[i]
print(a)
return a
if x < n-x:
print(a)
for i in range(x):
a[i], a[(i-x+n) % n] = a[(i-x+n) % n], a[i]
print(a)
rotate(a[:n-x],x)
else:
print(a)
for i in range(n-x):
a[i], a[(i-(n-x) + n) % n] = a[(i-(n-x) + n) % n] , a[i]
print(a)
rotate(a[n-x:], n-x)
rotate(a,x)
print(a)
I am getting the right values at each stage but the recursive function call is not returning the expected result and I cannot seem to understand the cause. Can someone explain whats wrong with my recursion ? and what can be the possible alternative.
You can rotate a list in place in Python by using a deque:
>>> from collections import deque
>>> d=deque([1,2,3,4,5])
>>> d
deque([1, 2, 3, 4, 5])
>>> d.rotate(2)
>>> d
deque([4, 5, 1, 2, 3])
>>> d.rotate(-2)
>>> d
deque([1, 2, 3, 4, 5])
Or with list slices:
>>> li=[1,2,3,4,5]
>>> li[2:]+li[:2]
[3, 4, 5, 1, 2]
>>> li[-2:]+li[:-2]
[4, 5, 1, 2, 3]
Note that the sign convention is opposite with deque.rotate vs slices.
If you want a function that has the same sign convention:
def rotate(l, y=1):
if len(l) == 0:
return l
y = -y % len(l) # flip rotation direction
return l[y:] + l[:y]
>>> rotate([1,2,3,4,5],2)
[4, 5, 1, 2, 3]
>>> rotate([1,2,3,4,5],-22)
[3, 4, 5, 1, 2]
>>> rotate('abcdefg',3)
'efgabcd'
For numpy, just use np.roll
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> np.roll(a, 1)
array([9, 0, 1, 2, 3, 4, 5, 6, 7, 8])
>>> np.roll(a, -1)
array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])
Or you can use a numpy version of the same rotate above (again noting the difference in sign vs np.roll):
def rotate(a,n=1):
if len(a) == 0:
return a
n = -n % len(a) # flip rotation direction
return np.concatenate((a[n:],a[:n]))
A simple and shorthand syntax for array rotation in Python is
arr = arr[numOfRotations:]+arr[:numOfRotations]
Example:
arr = [1,2,3,4,5]
rotations = 4
then
arr = arr[4:]+arr[:4]
gives us
[5,1,2,3,4]
I found a problem that I needed Right and Left rotations for big values of k (where k is number of rotations), so, I implemented the following functions for any size of k.
Right Circular Rotation (left to the right: 1234 -> 4123):
def right_rotation(a, k):
# if the size of k > len(a), rotate only necessary with
# module of the division
rotations = k % len(a)
return a[-rotations:] + a[:-rotations]
Left Circular Rotation (right to the left: 1234 -> 2341):
def left_rotation(a, k):
# if the size of k > len(a), rotate only necessary with
# module of the division
rotations = k % len(a)
return a[rotations:] + a[:rotations]
Sources:
https://stackoverflow.com/a/46846544/7468664
https://stackoverflow.com/a/9457923/7468664
Do you actually need to implement the block swap or are you just looking to rotate the array? In python you can do CW and CWW rotations using
zip(*arr[::-1])
and
zip(*arr)[::-1]
I expect that when you pass a slice of a to your recursive call, you're not passing the same variable any more. Try passing a in its entirety and the upper / lower bounds of your slice as additional arguments to your function.
For instance consider this function:
def silly(z):
z[0] = 2
I just tried the following:
>>> z = [9,9,9,9,9,9]
>>> silly(z)
>>> z
[2, 9, 9, 9, 9, 9]
>>> silly(z[3:])
>>> z
[2, 9, 9, 9, 9, 9]
Where you can see the modification to the slice was not retained by the full array
Out of curiosity, what outputs do you get & what outputs do you expect?
you can use this code for left rotation in python array
import copy
def leftRotate(arr,n) :
_arr = copy.deepcopy(arr)
for i in range(len(arr)-n):
arr[i] = arr[i+n]
for j in range(n):
arr[(len(arr)-n)+j] = _arr[j]
arr = [1, 2, 3, 4, 5, 6, 7]
leftRotateby = 3
leftRotate(arr,leftRotateby)
print arr
#output:: [4, 5, 6, 7, 1, 2, 3]
def leftRotation():
li = [int(x) for x in input().split()]
d = int(input())
ans = (li[d:]+li[0:d])
for i in ans:
print(i,end=' ')
print()
leftRotation()
def rotLeft(a, d):
lenght=len(a)
for j in range(0,d):
temp = a[0]
for i in range(lenght-1):
a[i] = a[i+1]
a[lenght-1] = temp
# Write your code here
return a
if __name__ == '__main__':
fptr = open(os.environ['OUTPUT_PATH'], 'w')
first_multiple_input = input().rstrip().split()
n = int(first_multiple_input[0])
d = int(first_multiple_input[1])
a = list(map(int, input().rstrip().split()))
result = rotLeft(a, d)
fptr.write(' '.join(map(str, result)))
fptr.write('\n')
fptr.close()
Array Rotation:-
print("Right:1,Left:2")
op=int(input())
par=[1,2]
if op not in par:
print("Invalid Input!!")
else:
arr=list(map(int,input().strip().split()))
shift=int(input())
if op ==1:
right=arr[-shift:]+arr[:-shift]
print(right)
elif op==2:
left=arr[shift:]+arr[:shift]
print(left)
Ouput:-`
Right:1,Left:2
1
12 45 78 91 72 64 62 43
2
[62, 43, 12, 45, 78, 91, 72, 64]`
def rotate(nums=[1,2,3,4,5,6,7], k=3):
i=0
while i < k:
pop_item = nums.pop()
nums.insert(0, pop_item)
i += 1
return nums # [5,6,7,1,2,3,4]
If you are not supposed to use deque or slicing:
def rotate(array: List[int], k: int) -> List[int]:
# loop through the array from -k to array_size - k
return [array[i] for i in range(-k, len(array) - k)]
def rotate(nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: None Do not return anything, modify nums in-place instead.
"""
result = []
if len(nums) <= 1 : return nums
if k > len(nums): k = K % len(nums)
for i in range(k):
result.insert(0,nums[-1])
nums.pop()
nums = result + nums
return nums
Here, I've used the method of swapping using a temporary variable to rotate an array. As an example, an array 'a' of size 5 is considered. Two variables 'j' and 'i' are used for outer and inner loop iterations respectively. 'c' is the temporary variable. Initially, the 1st element in the array is swapped with the last element in the array i.e a=[5,2,3,4,1]. Then, the 2nd element is swapped with the current last element i.e in this case 2 and 1. At present, the array 'a' would be a=[5,1,3,4,2]. This continues till it reaches the second last element in the array. Hence, if the size of the array is n (5 in this case), then n-1 iterations are done to rotate the array. To enter the no of times the rotation must be done, m is given a value with which the outer loop is run by comparing it with the value of j.
Note: The rotation of array is towards the left in this case
a=[1,2,3,4,5]
n=len(a)
m=int(input('Enter the no of rotations:'))
j=0
while(j<m):
i=0
while(i<n):
c=a[i]
a[i]=a[n-1]
a[n-1]=c
i=i+1
j=j+1
print(a)
This question already has answers here:
Efficient way to rotate a list in python
(27 answers)
Closed last month.
I'd like to rotate a Python list by an arbitrary number of items to the right or left (the latter using a negative argument).
Something like this:
>>> l = [1,2,3,4]
>>> l.rotate(0)
[1,2,3,4]
>>> l.rotate(1)
[4,1,2,3]
>>> l.rotate(-1)
[2,3,4,1]
>>> l.rotate(4)
[1,2,3,4]
How might this be done?
def rotate(l, n):
return l[-n:] + l[:-n]
More conventional direction:
def rotate(l, n):
return l[n:] + l[:n]
Example:
example_list = [1, 2, 3, 4, 5]
rotate(example_list, 2)
# [3, 4, 5, 1, 2]
The arguments to rotate are a list and an integer denoting the shift. The function creates two new lists using slicing and returns the concatenatenation of these lists. The rotate function does not modify the input list.
If applicable, you could use collections.deque as a solution:
import collections
d = collections.deque([1,2,3,4,5])
d.rotate(3)
print d
>>> deque([3, 4, 5, 1, 2])
As a bonus, I'd expect it to be faster than in-built list.
The following function will rotate the list l, x spaces to the right:
def rotate(l, x):
return l[-x:] + l[:-x]
Note that this will only return the original list if x is outside the range [-len(l), len(l)]. To make it work for all values of x, use:
def rotate(li, x):
return li[-x % len(li):] + li[:-x % len(li)]
>>> l=[1,2,3,4]
>>> l[1:]+l[:1]
[2, 3, 4, 1]
>>> l=[1,2,3,4]
>>> l[2:]+l[:2]
[3, 4, 1, 2]
>>> l[-1:]+l[:-1]
[4, 1, 2, 3]
A general rotate n to the left (positive y in the call to rotate) or right (negative y) then:
def rotate(l, y=1):
if len(l) == 0:
return l
y = y % len(l) # Why? this works for negative y
return l[y:] + l[:y]
If you want the direction of rotation to be the same as your example, just negate y in rotate.
def rotate(l, y=1):
if len(l) == 0:
return l
y = -y % len(l) # flip rotation direction
return l[y:] + l[:y]