Order of execution in nested loops in python [duplicate] - python

This question already has answers here:
Why does python use 'else' after for and while loops?
(24 answers)
Why does this `else` block work yet it is not on the same level as the `if` case? [duplicate]
(3 answers)
Closed 3 years ago.
I cant understand the difference between those two code blocks, first the correct one is:
number_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
prime_list=[]
for i in number_list:
for j in range(2,i):
if i%j==0:
break
else: prime_list.append(i)
print('Primes are:',prime_list)
Output is:
Primes are: [1, 2, 3, 5, 7, 11, 13]
but moving the else statement forward inside the block below the if statement (which i thought was the right thing to do) results in a different and wrong output:
number_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
prime_list=[]
for i in number_list:
for j in range(2,i):
if i%j==0:
break
else: prime_list.append(i)
print('Primes are:',prime_list)
Output is:
Primes are: [3, 5, 5, 5, 7, 7, 7, 7, 7, 9, 11, 11, 11, 11, 11, 11, 11, 11, 11, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13]
Why is this so? how does the code behave with the else statement indented?

In the first example else is used for: for loop. It means that the code inside else statement is executed only if for loop ends without break.
So in your case only of any number from the range (2, i) is not a divider of i.
In the second case else is used for: if statement. It means that: if if is not true, then execute else statement. In this scenario let's assume i=5. For:
j = 2 => i%j = 1 => else statement is executed: prime_list.append(5)
j = 3 => i%j = 2 => else statement is executed: prime_list.append(5)
j = 4 => i%j = 1 => else statement is executed: prime_list.append(5)
And thats why you have 3 times 5 in your list.

The else in different scope has difference means:
else with the same scope of your for-loop is the else condition. It will be execute when not-break in your for loop.
else in the for-loop is used to exit the for loop in your second case. The else is related on your if statement

Because when moving in, it will loop and most of the case exit the first if statement, then since the else says to add the value, it would keep adding to it.
There is other ways to do this and more efficiently, like below:
prime_list=[i for i in number_list if all(i % x for x in range(2, i))][1:]
print('Primes are:',prime_list)

Related

removing negative numbers from a list without creating an new one [duplicate]

This question already has answers here:
How to remove items from a list while iterating?
(25 answers)
Closed 1 year ago.
In this task I can only write the function and the rest of the code is given, so the original list l in the function should be changed without creating a new one. However, as I understood it when I already read through similar questions, this is not a good idea at all.
My try looked like this:
import random
def removeNegatives(listOfIntegers):
for i in listOfIntegers:
if i < 0:
listOfIntegers.remove(i)
#code can not be changed from here on!
l = []
for i in range(0, random.randint(15,25)):
l.append(random.randint(-15,15))
print ("Before:", l)
removeNegatives(l)
print ("After:", l)
As you need to change the list in place, you can loop in reverse to avoid affecting the order while poping elements:
def removeNegatives(listOfIntegers):
for pos in range(len(listOfIntegers), 0, -1):
if listOfIntegers[pos-1] < 0:
listOfIntegers.pop(pos-1)
example output:
Before: [15, 7, -1, 3, -6, -2, 10, -1, 4, 5, -10, -8, 9, 9, 13, -11, 1, -4, -9, -15, -8, 3, 1]
After: [15, 7, 3, 10, 4, 5, 9, 9, 13, 1, 3, 1]
NB. it is more efficient to change the list only once, you can use the following code to overwrite the full list with new (filtered) elements:
def removeNegatives(listOfIntegers):
listOfIntegers[:] = filter(lambda i: i>0, listOfIntegers)

The folding of ternary operator in a list

The following code is available (demo)
f=lambda m, x:m and(x&1 and m.pop(0)or m.pop(0)[::-1])+f(m, x+1)
print(f([[4, 3, 2, 1], [5, 6, 7, 8], [12, 11, 10, 9], [13, 14, 15, 16]],0))
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
Here there is logic:
m.pop(0) if x&1 else m.pop(0)[::-1]
Please explain why when folding to the following view, is the code not executing correctly?
[m.pop(0)[::-1],m.pop(0)][x&1]
I don't know much about Python, will be grateful for any help, thank you.
UPD: If I change the logic, I get this result:
f=lambda m,x:m and([m.pop(0)[::-1],m.pop(0)][x&1])+f(m,x+1)
print(f([[4, 3, 2, 1], [5, 6, 7, 8], [12, 11, 10, 9], [13, 14, 15, 16]],0))
# [1, 2, 3, 4, 13, 14, 15, 16]
PS. Code-golf (If this is important, the essence of the code is that it bypasses the two-dimensional array in the form of a snake)
Solution:
[m.pop(0)[::-1],m.pop(0)][x&1] => (lambda: m.pop(0)[::-1], lambda: m.pop(0))[x&1]().
https://ideone.com/u6Qp4O
The issue is that the trinary if only evaluates one of its branch so only one pop call occurs. In your code there are two pop calls that both are evaluated.
[m.pop(0)[::-1], m.pop(0)][x&1]
To avoid that, if one must write it this way, and not using an a trinary A if C else B, one has to thunk, lambda:, each case and then after indexing the list of cases, call the case that was selected:
[lambda: m.pop(0)[::-1], lambda: m.pop(0)][x&1]()
One then could remove the common expression:
item = m.pop(0)
[lambda: item[::-1], lambda: item][x&1]()
Doing this before thunking, would result in:
item = m.pop(0)
[item[::-1], item][x&1]
This would not produce an error if item was slice-able. But it would uselessly produce and discard a reversed copy of item. But if the item is say an int when x is odd and a list when even this would result in an error:
>>> 3[::-1]
TypeError: 'int' object has no attribute '__getitem__'
Because all sub expressions in an expression are evaluated unless they have been delayed by a lambda.
A thunk is a term for a function that takes no arguments.
The short-circuiting behavior is different between all three versions of the expression given (if certain elements of m are false). The a and b or c idiom was commonly used before Python had a conditional operator, but is inequivalent if b is false.

Working around evaluation time discrepancy in generators

I found myself running into the gotcha under 'evaluation time discrepancy' from this list today, and am having a hard time working around it.
As a short demonstration of my problem, I make infinite generators that skip every nth number, with n going from [2..5]:
from itertools import count
skip_lists = []
for idx in range(2, 5):
# skip every 2nd, 3rd, 4th.. number
skip_lists.append(x for x in count() if (x % idx) != 0)
# print first 10 numbers of every skip_list
for skip_list in skip_lists:
for _, num in zip(range(10), skip_list):
print("{}, ".format(num), end="")
print()
Expected output:
1, 3, 5, 7, 9, 11, 13, 15, 17, 19,
1, 2, 4, 5, 7, 8, 10, 11, 13, 14,
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
Actual output:
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
Once I remembered that great feature, I tried to "solve" it by binding the if clause variable to a constant that would be part of the skip_list:
from itertools import count
skip_lists = []
for idx in range(2, 5):
# bind the skip distance
skip_lists.append([idx])
# same as in the first try, but use bound value instead of 'idx'
skip_lists[-1].append(x for x in count() if (x % skip_lists[-1][0]) != 0)
# print first 10 numbers of every skip_list
for skip_list in (entry[1] for entry in skip_lists):
for _, num in zip(range(10), skip_list):
print("{}, ".format(num), end="")
print()
But again:
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
1, 2, 3, 5, 6, 7, 9, 10, 11, 13,
Apart from an actual solution, I would also love to learn why my hack didn't work.
The value of idx is never looked up until you start iterating on the generators (generators are evaluated lazily), at which point idx = 4 the latest iteratee value, is what is present in the module scope.
You can make each appended generator stateful in idx by passing idx to a function and reading the value from the function scope at each generator's evaluation time. This exploits the fact that the iterable source of a generator expression is evaluated at the gen. exp's creation time, so the function is called at each iteration of the loop, and idx is safely stored away in the function scope:
from itertools import count
skip_lists = []
def skip_count(skip):
return (x for x in count() if (x % skip) != 0)
for idx in range(2, 5):
# skip every 2nd, 3rd, 4th.. number
skip_lists.append(skip_count(idx))
Illustration of generator expression's iterable source evaluation at gen. exp's creation:
>>> (i for i in 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable
Your case is a bit trickier since the exclusions are actually done in a filter which is not evaluated at the gen exp's creation time:
>>> (i for i in range(2) if i in 5)
<generator object <genexpr> at 0x109a0da50>
The more reason why the for loop and filter all need to be moved into a scope that stores idx; not just the filter.
On a different note, you can use itertools.islice instead of the inefficient logic you're using to print a slice of the generator expressions:
from itertools import islice
for skip_list in skip_lists:
for num in islice(skip_list, 10):
print("{}, ".format(num), end="")
print()

How to I write a recursive function for split even and odd indices of array?

This is question for my interview.
Write a recursive function that does the following:
Input: An array A of length N. N is an even number and N >= 2.
Output: A reordered array B. The first half of B contains A’s elements with even indices. The second half of B contains A’s elements with odd indices. Convention: the first index of an array is 0 (and thus it is an even number).
Input 1: [4, 8, 12, 16]
For this array, the indices and the values are as follows:
Index: 0, 1, 2, 3
Value: 4, 8, 12, 16
Thus, the output is as follows:
Expected output 1: [4, 12, 8, 16]
ADDITIONAL TEST CASE
Input 2: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Expected output 2: [1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
TASK
Write a recursive function in a programming language of your choice (as if you are writing real code to be used on a production server) for the above problem
In addition to the main function, you are free to write helper functions (if needed)
The code should have as few lines as possible (but it should still be clear and readable)
Note: Your recursive function must show the 'spirit' of a recursive function (not just the recursive form of a for loop)
Here is my code:
def slove(array, deep=0):
'''para:
array: list input.
return: list.
!!!Do not set value for deep!!!'''
if len(array) > 2:
if deep > 0:
for i in xrange(0, len(array), 2):
array[i], array[i + 1] = array[i + 1], array[i]
left = array[0]
right = array[-1]
array = array[1:-1]
array = slove(array, deep + 1)
array.insert(0, left)
array.append(right)
return array
else:
array[0], array[-1] = array[-1], array[0]
return array
if __name__ == '__main__':
array = map(int, raw_input('Enter array with sep is space key: ').split(' '))
# array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print slove(array)
He said that it is wrong because I use loop in program. He is correct? So how to solve it?
Why not just use slicing?
lst = [11,12,13,14,15,16]
lst[0::2] + lst[1::2]
Returns:
[11, 13, 15, 12, 14, 16]
This pseudocode function might help:
Let A[n] and B[n] be the 2 arrays where n is the size.
Then we will call the following method rearrange(0,0):
rearrange(int i, int j) {
b[j] = a[i];
b[j+n/2] = a[i+1];
if (j < n/2-1)
rearrange(i+2,j+1);
}
In this method, i jumps 2 times each therefore the odd items get stored in the first half of the output array. For the second half, j+n/2 saves the even items.
This is one (awkward) way to do it:
def even_odd_split(seq):
"""
>>> even_odd_split([4, 8, 12, 16])
[4, 12, 8, 16]
>>> even_odd_split([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
[1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
"""
assert(len(seq) % 2 == 0)
assert(len(seq) > 1)
def _split(seq):
if len(seq) == 0: return [], []
a, b = _split(seq[2:])
return [seq[0]] + a, [seq[1]] + b
a, b = _split(seq)
return a + b
if __name__ == '__main__':
import doctest
doctest.testmod()
I would solve it like this:
def f(l):
if len(l) == 1:
# only one element left
return l
if len(l) % 2 == 0:
# length is even
return l[:1] + f(l[1:])
else:
# length is odd
return f(l[1:]) + l[:1]
Every invocation of the function removes one element from the list and either puts it at the beginning or at the end of the resulting list.
It does not produce the given “expected” outputs, because the elements of the list appear in another order than they appear in the input list, but it matches the specification… and also, I think it’s pretty simple.

Restart an iteration of a for loop in python

I am am looking to restart an iteration if i receive an exception. (... it is reading data from a server, and occasionally gets intermittent error codes, that do not repeat on a retrying).
with open(input, 'rb') as f:
r = unicodecsv.reader(f)
for row in r:
code to request some data from server
if response_code == 200:
code to process response
else:
want to restart the iteration for the current row
If i was using a while loop, this sort of thing would obviously be obvious (e.g. don't increment the number), but given I am iterating over row in a for loop, can't think of a way of forcing a re-do of the current iteration.
Although there are lots of similar sounding titled posts (e.g. how to restart "for" loop in python ? , Python - Way to restart a for loop, similar to "continue" for while loops? , python: restarting a loop ) each of the ones I have found/read seems to get at something different (e.g. just how to restart when get to the end, rather than restarting an iteration should a certain condition occur).
[Python 2.7]
You can add a second loop like so:
for row in r:
while True:
do stuff
if error:
continue
else:
break
This will restart the for-loop at 4 when the iteration reaches 5 and continue with seemingly no break.
def testloop(start, fault_data, maxrange=10, reset_fault_data=0):
for x in range(start, maxrange):
if x == fault_data:
x -= 1
testloop(x+1, reset_fault_data, maxrange)
break
print(x, end=", " if not x == maxrange-1 else " ")
testloop(1, 5, 21)
output:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
without this:
testloop(x+1, reset_fault_data, maxrange)
break
output:
1, 2, 3, 4, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20

Categories