How to break while loop - python

I am trying to find if a element in a list exists in a range where the list size is known and number of ranges is also known
I have tried a logic in python using a while loop
a[1]=[23,330,460]
r=[1,120,300,450,600]
and my range list are
1 to 100 for i=0
120 to 220 for i=1
300 to 400 for i=2
450 to 550 for i=3
600 t0 700 for i=4
a[1] has some values which is compared with the elements in range
a[0] is output got after checking
if a[1] is not in the range defined
a[0] must be 0,
else 1
j=0
for i in range(0,5):
while j<3:
l=list(range(r[i],r[i]+100))
if a[1][j] in l:
a[0].append(1)
j=j+1
else:
a[0].append(0)
j=j
I expect the output to be a[0]=[1 0 1 1 0]

Your code does not change 'j' value in else since it executes first in this case,so it becomes infinite.
a=[[],[23,330,460]]
r=[1,120,300,450,600]
for i in range(5):
j=0
l=list(range(r[i],r[i]+100))
while j<3:
if a[1][j] in l:
a[0].append(1)
break
j=j+1
else:
a[0].append(0)
print(a[0])
#[1, 0, 1, 1, 0]
Using for
a=[[],[23,330,460]]
r=[1,120,300,450,600]
for i in range(5):
l=list(range(r[i],r[i]+100))
for j in a[1]:
if j in l:
a[0].append(1)
break
else:
a[0].append(0)
print(a[0])
#[1, 0, 1, 1, 0]
Using any
Updated as per tripleee answer, range is not necessary
a=[[],[23,330,460]]
r=[1,120,300,450,600]
for i in range(5):
a[0].append(int(any(r[i]<=j<=r[i]+100 for j in a[1])))
print(a[0])
#[1, 0, 1, 1, 0]

What about if you use another if: if the result is what you expect, keep going. If not, break.
result = []
for pos, val in enumerate(a[1]):
if r[pos] <= val <= r[pos]+100:
comp = 1
else:
if comp == 1 then
echo 'Well done'
else break 0;
result.append(comp)
Not pretty sure btw...

Creating a list of 100 elements on each iteration over the loop is extremely inefficient and unidiomatic. I think you want simply
result = []
for pos, val in enumerate(a[1]):
if r[pos] <= val <= r[pos]+100:
comp = 1
else:
comp = 0
result.append(comp)
or more succinctly with a list comprehension
result = [1 if r[pos] <= val <= r[pos]+100 else 0
for pos, val in enumerate(a[1])]
If you want the value to be in a[0] then simply assign a[0] = result at the end; but really, this design is extremely dubious. I would suggest you keep two separate variables, or possibly refactor the input and output into a class if you need to keep them together.

Related

loop through numbers and evaluate if numbers are divisible by certain numbers

i wrote a program to evaluate what numbers in a certain range are divisble by only certain numbers (in the range 1 to 9). So far the code seems to work but i tested the steps it takes through at pythontutor http://www.pythontutor.com/visualize.html#mode=edit and something odd happened.
In the second loop, the code doesnt always check all values (k) for its divisibility but sometimes leaves out the last value (k).
Better to give you an example:
Liste = []
for i in range (1500, 1700):
if i%5 == 0 and i%7 == 0:
Liste.append(i)
print (Liste)
Teiler = [2, 3, 4, 6, 8, 9]
for k in Liste:
z = 0
for w in range (len(Teiler)):
if k%Teiler[z] == 0:
Liste.remove(k)
z += 1
break
else:
z += 1
print (Liste)
Here the output is as follows:
[1505, 1540, 1575, 1610, 1645, 1680]
[1505, 1575, 1645]
And its the output that its supposed to be, as in the second list are only the numbers divisible by 5 and 7. Also on pythontutor the second for loop goes through all values (k).
But when i change the range of number like the following (line 2, change range from 1700 to 1800):
Liste = []
for i in range (1500, 1800):
if i%5 == 0 and i%7 == 0:
Liste.append(i)
print (Liste)
Teiler = [2, 3, 4, 6, 8, 9]
for k in Liste:
z = 0
for w in range (len(Teiler)):
if k%Teiler[z] == 0:
Liste.remove(k)
z += 1
break
else:
z += 1
print (Liste)
the output is as the following:
[1505, 1540, 1575, 1610, 1645, 1680, 1715, 1750, 1785]
[1505, 1575, 1645, 1715, 1785]
If i run this code on pythontutor, the code stops at k = 1750 and doesnt check for k = 1785.
Why is it behaving that way?
Is it a problem with the check on pythontutor?
Or is there a problem with my code?
I want to understand why Python is doing what it does.
Thank you very much for your help. Iam sorry if this is a noob question or iam missing out something obvious.
Your problem is in this line:
Liste.remove(k)
because you are removing element from the same list that you use for the for loop so when you delete an element the list is going to be shorter but next iteration you will jump an element.
I advice to use two list or use a while loop and when you remove the element from list you don't go to next iteration and you decrease the len of the list of 1; example:
Liste = []
for i in range (1500, 1800):
if i%5 == 0 and i%7 == 0:
Liste.append(i)
print (Liste)
Teiler = [2, 3, 4, 6, 8, 9]
l=len(Liste)
i=0
while i<l:
k=Liste[i]
z = 0
for w in range (len(Teiler)):
if k%Teiler[z] == 0:
Liste.remove(k)
l-=1
i-=1
z += 1
break
else:
z += 1
i+=1
This code could be improved but was for let you understand how you can do it
Don't remove items from the list you iterate over, as the other answer states. Changing the length of the list affects the iteration. Example:
>>> L=list(range(10))
>>> for i in L:
... if i==5: L.remove(i)
... print(i)
...
0
1
2
3
4
5 # removed 5, and skipped 6!
7
8
9
Also w is never used, although z is the equivalent. Instead, iterate directly over Teiler and add items to a new list if it satisfies your condition:
Liste = []
for i in range (1500, 1800):
if i%5 == 0 and i%7 == 0:
Liste.append(i)
print (Liste)
Liste2 = []
Teiler = [2, 3, 4, 6, 8, 9]
for k in Liste:
for w in Teiler:
if k % w == 0:
break
else:
Liste2.append(k)
print(Liste2)
Output:
[1505, 1645, 1715]
If you haven't seen for/else, the else only executes if you don't break from the for loop, so all of k % w != 0 has to be true.
Another option is to use list comprehensions, which really simplify the code:
L = [i for i in range(1500,1800) if i%5 == 0 and i%7 == 0]
L = [x for x in L if all(x % k != 0 for k in (2,3,4,6,8,9))]
print(L)
Output:
[1505, 1645, 1715]
Note 1575 and 1785 are divisible by 3, so both your sample lists had errors from removing values in the list.

Index out of bound error for comparing adjacent numbers in list

List item
for i in num_list:
if(i < len(num_list) - 1 and num_list[i] == num_list[i+1]):
count = count + 1
else:
count = count
I'm getting index out of bound error in if condition. What is the error? I have used len in if condition
Your problem is that the i value is not the indices of the list, it is the actual items in the list that are being iterated through.
Your else statement in the loop is also not necessary since it does not change anything.
I think this accomplishes what you are looking for:
count = 0
num_list = [1, 2, 3, 3, 2, 1, 1]
for i in range(len(num_list) - 1):
if(num_list[i] == num_list[i + 1]):
count += 1
print(count)
# Output: 2
range(len(num_list) - 1) is going to loop through all indices except for the last one in the list. So the if check to make sure it is not out of bounds is not necessary.
for i in num_list:
does not iterate over the length/range, instead if iterates over the values.
For example
num_list = [3,4,1,5,3]
for i in num_list:
print(i)
would output
3
4
1
5
3
Instead, you want to iterate over the index in which case your foor loop should look like this:
for i in range(len(num_list)):
where the following code
num_list = [3,4,1,5,3]
for i in num_list:
print(i)
would output
0
1
2
3
4

How do I loop through two lists only iterating one at a time?

Im trying to implement merge sort in python and I'd like to know what is the pythonic way of looping through two lists iterating only one of the lists each loop.
This is what I have now (using indexes)
def merge(array1, array2):
final = []
i = 0
j = 0
while i < len(array1) or j < len(array2):
if array1[i] <= array2[j]:
final.append(array1[i])
i += 1
elif array2[j] < array1[i]:
final.append(array2[j])
j += 1
# Finished one of the arrays
if i == len(array1):
final.extend(array2[j:])
break
elif j == len(array2):
final.extend(array1[i:])
break
return final
Thanks.
I'm not sure not sure your problem here is using indices, but there's a lot of unneeded code.
Firstly, we can change your initialisers to:
final, i, j = [], 0, 0
Next, if we change your while condition we can remove the breaks:
while i < len(array1) and j < len(array2):
Your elif isn't meaningful as it will always be true, we can thus make your if:
if array1[i] <= array2[j]:
final.append(array1[i])
i += 1
else:
final.append(array2[j])
j += 1
And now because we're automatically breaking out of the loop we don't need the breaks and can move the extends outside:
def merge(array1, array2):
final, i, j = [], 0, 0
while i < len(array1) and j < len(array2):
if array1[i] <= array2[j]:
final.append(array1[i])
i += 1
else:
final.append(array2[j])
j += 1
final.extend(array1[i:])
final.extend(array2[j:])
return final
This gives you smaller, more readable code without actually changing the way you're doing anything, that way you'll still understand it.
Note that we can perform:
final.extend(array1[i:])
final.extend(array2[j:])
outside the loop because one array will have contents, for example [7,9], while the other will be empty([]):
>>> final = [3,6]
>>> array1 = [3,6]
>>> array2 = [7,9]
>>> i = 2
>>> j = 0
>>> array1[i:]
[]
>>> array2[j:]
[7, 9]
>>> final.extend(array1[i:])
>>> final
[3, 6]
>>> final.extend(array2[j:])
>>> final
[3, 6, 7, 9]

Subsetting python list into positive/negative movements/trends

Sorry for creating this question but I have been stuck on this question for a while.
Basically I'm trying to take a list:
numbers=[1, 2, -1, -2, 4, 5]
And subset this list into a list of list that display positive/negative movements (or trends)
The end result is to have:
subset_list = [[1, 2], [-1, -2], [4, 5]]
Basically I have been using nested while functions to append a positive movement to the subset, and when the condition is not met, the subset is appended to subset_list and then evaluates if there is a negative movement.
I keep getting an IndexError, and so far subset_list only contains [[1, 2]]
Here is my code:
numbers = [1,2,-1,-2,4,5]
subset = []
subset_list = []
subset.append(numbers[0])
i = 1
while i < (len(numbers)):
if numbers[i] <= numbers[i+1]:
subset.append(numbers[i])
i+= 1
while subset[-1] <= numbers[i] or numbers[i] <= numbers[i+1]:
subset.append(numbers[i])
i += 1
subset_list.append(subset)
subset = []
i += 1
if numbers[i] > numbers[i+1]:
subset.append(numbers[i])
i+= 1
while subset[-1] <= numbers[i] or numbers[i] <= numbers[i+1]:
subset.append(numbers[i])
i+= 1
subset_list.append(subset)
subset = []
i += 1
Thanks!
-Jake
Here is a way to re-write this:
numbers=[1,2,-1,-2,4,5]
direction = True # positive or negative
prevdirection = True
res = [[numbers[0]]]
for previtem, item in zip(numbers[:-1], numbers[1:]):
direction = True if item - previtem > 0 else False
if direction != prevdirection:
res.append([])
prevdirection = direction
res[-1].append(item)
print(res)
In python, one tends not to use the actual indexes in a list very often. Try a for-loop instead, plus a check to see whether the trend changed or not (this treats zero as a distinct trend from positive or negative - you can pretty simply change same_direction to group it one way or the other):
def same_direction(num1, num2):
# both numbers are positive, both are negative, or both are zero
return ((num1 > 0 and num2 > 0) or
(num1 < 0 and num2 < 0) or
(num1 == num2))
numbers = [1, 2, -1, -2, 4, 5]
result = [[]] #list with one sublist ready
last_number = 0
for num in numbers:
if same_direction(num, last_direction):
# No need for a new sublist, put new number in last sublist
result[-1].append(num)
else:
# trend changed, new sublist and put the number in it
result.append([num])
If change in trends always go through the sign change, you can "group" items based on a sign using itertools.groupby():
>>> from itertools import groupby
>>>
>>> [list(v) for _, v in groupby(numbers, lambda x: x < 0)]
[[1, 2], [-1, -2], [4, 5]]
We are using _ as a variable name for a "throw-away" variable since we don't need the grouping key in this case.
This is what I came up with. It is close to what you have but a little easier to read. I avoid having to increment the index counter i as much which is probably where you went wrong.
n= [1,2,-1,-2,4,5]
out=[]
i=1
tmp=[n[0]]
while i < len(n):
if n[i] >= 0 and tmp[-1] >= 0:
tmp.append(n[i])
elif n[i] < 0 and tmp[-1] < 0:
tmp.append(n[i])
else:
out.append(tmp)
tmp = [n[i]]
i = i + 1
if len(tmp) > 0: # typo fix was > 1
out.append(tmp)
print(out)
Here is my solution:
numbers = [1,2,-1,-2,4,5, 3, 2]
subset = []
subset_list = []
subset.append(numbers[0])
forward = 1
for i in range(0, len(numbers) - 1):
if ( forward == 1 ):
if numbers[i] <= numbers[i+1]:
subset.append(numbers[i+1])
else:
subset_list.append(subset)
subset = []
subset.append(numbers[i+1])
forward = 0
else:
if numbers[i] >= numbers[i+1]:
subset.append(numbers[i+1])
else:
subset_list.append(subset)
subset = []
subset.append(numbers[i+1])
forward = 1
subset_list.append(subset)
print(*subset)
print(*subset_list)
Unfortunately, I only have python 3 on my system so my answer is in python 3.

Implementing Insertion Sort in Python with For Loops only

I tried implementing Insertion sort with for loops only and wrote the following code:
def isort(L): #implementation with a for loop
for i in range(1,len(L)):
small = L[i]
M = range(i)
M.reverse()
for j in M:
if small<L[j]:
L[j+1]=L[j]
else:
break
L[j+1] = small
return L
L = [5,4,3,2,1]
M = isort(L)
print M
This gives the output [5,1,2,3,4]. Can someone please point out where I am making a mistake
Change (the fix shown in the question is easy, the one-off error was caused by one little +1 :)):
L[j+1] = small
To:
L[j] = small
Testing:
>>> isort([5, 4, 3, 2, 1])
[1, 2, 3, 4, 5]
However, there are some other things with your code, as illustrated- it will not work alot of the time. With a fair few tweaks, we can get it to work:
def isort(L):
for i in range(1,len(L)):
small = L[i]
M = range(-1, i)
M.reverse()
for j in M:
if j>=0 and small<L[j]:
L[j+1]=L[j]
else:
break
L[j+1] = small
return L
Testing:
>>> isort([4, 5, 3, 2, 1])
[1, 2, 3, 4, 5]
The post condition for the inner loop is that j is pointing for the first value that is smaller than small (this is achieved by the break call). However, the loop naturally exists when j=0, therefore in every last inner iteration, the condition is not what you'd expect.
To fix it, I suggest initializing M from -1:
M = range(-1, i)
But then, you have to check as well that j is positive (to avoid making changes you don't want to):
if j>=0 and small<L[j]:
L[j+1]=L[j]
This is little tricky :
I took the inner loop range function as range(j, -2, -1) , so the inner loop always breaks at one position ahead, so the last statement arr[j + 1] = key works perfectly
arr = [5, 4, 3, 2, 1]
for i in range(1, len(arr)):
j = i - 1
key = arr[i]
for j in range(j, -2, -1):
if j < 0 or key >= arr[j]:
break
else:
arr[j + 1] = arr[j]
arr[j + 1] = key
if __name__ == "__main__":
n = int(input("How many numbers ?\t"))
nums = [int(x) for x in input("Enter {} numbers\t".format(n)).split()]
for i in range(1,n):
val = nums[i]
for j in range(i-1,-2,-1):
if j < 0 : break
if nums[j] > val:
nums[j+1] = nums[j]
else:
break
nums[j+1] = val
for num in nums:
print(num,end=' ')
print()

Categories