Python SUM_WITH_FLAG - python

I entered to my college newly in recent and my major is CS.
I am very struggling with python programming and I need you guys help very seriously!
def sum_with_flag(alist,flag):
''' sum_with_flag(list,int) -> int
Returns the sum of all elements in the list. When `flag` is
seen in the list, `flag` is not added to the list. Additionally
everything after `flag` is not added to the list until `flag` is
seen again. In general, a number is added to the list only if
it is not equal to flag and there are an even number of occurances
of flag before it.
For example, if `flag` is 12
[1,12,5,7,12,8,12,1]
The function returns 1+8 = 9.
1 is counted
12 is flag, so 5 and 7 are not counted
12 is flag, so we count 8
12 is flag, so we do not count 1
sum_with_flag([1,12, 5, 7, 12, 8, 12, 1], 12)
9
sum_with_flag([1, 2, 3, 4], 0)
10
sum_with_flag([1, 2, 3, 4], 2)
1
sum_with_flag([1, 2, 1, 2, 1], 2)
2
sum_with_flag([1, 2, 1], 1)
0
sum_with_flag([1, 2, 1, 2, 1, 2, 1, 2, 1], 1)
4
'''
And here is my code.
for each in alist:
if each == flag:
del alist[flag:flag]
return sum(alist)
What is wrong with mine??
My second code is (it is not completed yet. Because I don't know what to change.)
A = True
for each in alist:
if each == flag:
A = not A
return sum(alist)

Your function is destructive: it will modify the list passed into it. It is generally bad form to do so unless your function's purpose is to modify the argument.
In alist[flag:flag], flag is treated as an index, but it is a value - it can be outside the index range, and even if not, it doesn't target anything that fits the requirements. E.g. in sum_with_flag([1,12, 5, 7, 12, 8, 12, 1], 12), alist doesn't even have 12 elements.
del alist[flag:flag] would delete any elements with flag <= index < flag, which is zero elements (given that no index is both less than flag and greater or equal to it at the same time).
The correct implementation would be to have a boolean variable that tells you whether you're including elements into a sum or not. It would start with a value True, indicating that you are. You also need a variable for the running sum, starting at zero. When you encounter an element that is equal to flag, you flip this variable. If an element is not equal to flag, then add it to the running sum, but only if you are currently in the adding mode.

Related

How to remove some elements from a list and append them at the beginning of the list in Python

Suppose that I have a list that has [0, 1, 2, 3 , 4, 5, 6] in it. I want to remove those elements that are greater than or equal to 3 and add those removed elements to the beginning of the list. So I wrote the code below:
list = [0, 1, 2, 3, 4, 5, 6]
new_list =[]
for number in list:
if number >= 3:
dropped_number = list.pop()
new_list.append(dropped_number)
new_list.sort()
new_list += list
print(new_list)
However, when I ran the code, the result was displayed as [5, 6, 0, 1, 2, 3 , 4]. Could anyone please explain to me at which step I did wrong here?
There are two issues with your code.
the number you obtain with list.pop() is not the one you just checked with your condition (it is merely the last one in the list)
When you reach 3, list.pop() removes 6,
When you reach 4, list.pop() removes 5,
You never reach 5 because you're at the end of what remains of the list at that point.
removing items from a list within a for-loop on the same list will cause the for-loop to skip items or complain that the list changed during iterations. So, even if you were to pop the appropriate number, your loop would miss items.
You also don't need to sort new_list every time you add to it, you can do it once at the end, but that just optimization.
Instead of a for-loop, you could use the sort method with a key parameter that returns a boolean indicating True for elements that do not meet your conditions (i.e that will be shifted to the right). Because Python's sort is stable, this will only place elements in two groups without otherwise changing their relative order.
L = [0, 2, 4, 6, 1, 3, 5]
L.sort(key=lambda x: not x>=3)
print(L) # [4, 6, 3, 5, 0, 2, 1]
If you need a more procedural solution, you can separate the values in two lists that you stick together at the end:
L = [0, 2, 4, 6, 1, 3, 5]
left,right = [], []
for x in L:
if x >= 3: left.append(x)
else: right.append(x)
L = left + right
# [4, 6, 3, 5, 0, 2, 1]
Modifying a list while iterating over it is usually problematic. What if instead you thought of the problem as building a new list out of two subsets of the original list?
>>> old_list = list(range(7))
>>> [i for i in old_list if i >= 3] + [i for i in old_list if i < 3]
[3, 4, 5, 6, 0, 1, 2]
The reason your program doesn't work is because you are modifying the list whilst searching through it. Instead, you can start by adding the elements >= 3 to a new list and then separately appending the elements < 3 to the list. Also, considering you are created a second 'new_list', there is no need to remove the elements from the first list.
Your new code:
list = [0, 1, 2, 3, 4, 5, 6]
new_list = []
# Append numbers greater than 3 to the new list
for number in list:
if number >= 3:
new_list.append(number)
# Append the numbers less than 3 to the new list
new_list += list[0:list.index(new_list[0])]
print(new_list)
Just to note, this method takes a section of the original list from position 0, to the position (.index) of the first item in the new list, which automatically generates the < 3 condition as the first item in the new list corresponds to the items before the >= 3 condition is met.
list[0:list.index(new_list[0])]

(Python) I need to remove the duplicate elements in a list (use remove funct; avoid typecasting). Trying to determine why my solution is incorrect

Specifications:
I want to use the remove function (in lists) and I'd prefer to avoid typecasting.
l = [2, 3, 3, 4, 6, 4, 6, 5]
q=len(l)
for i in range (0, q):
for g in range (i+1, q):
if l[g]==l[i]:
q-=1 #decremented q to account for reduction in list size.
l.remove(l[g])
print(l)
Error: if l[g]==l[i]:
IndexError: list index out of range
I know that similar questions have been asked by users previously. As the aforementioned constraints were absent in them, I would like to request you to treat this as a separate question. Thanks!
>>> l = [2, 3, 3, 4, 6, 4, 6, 5]
>>> s = set(l)
>>> t = sorted(s)
>>> print(t)
[2, 3, 4, 5, 6]
Using set is a simple and straight-forward way to filter your collection. If you don't need the list in a specific order, you can just use the set from thereon. The sorted function returns a list (using the default ordering).
Since you mentioned you don't want typecasting, so my solution is using while loop
l = [2, 3, 3, 4, 6, 4, 6, 5]
q=len(l)
i = 0
while i<len(l):
g = i+1
while (g < q):
if l[g]==l[i]:
q-=1 #decremented q to account for reduction in list size.
l.remove(l[g])
g += 1
i += 1
print(l)
Now, allow me to explain what was the problem in your code. When you use range function, it holds the starting and the ending value at the first run of the loop, so even if you change the limits afterwards in the loop, still, it won't change the range loop so eventually, you get index out of bounds error.
Hope this helps you :)
Your solution does not work, because range() store the value of q, and will ignore the change of q's value later. Eg:
>>> m = 10
>>> for i in range(m):
... m=0
... print(i)
...
0
1
2
3
4
5
6
7
8
9
Even if I change m, range() will still go 10 times in the loop. So, when you change the size of the list, even if you change q, you will still try to reach elements that does not exist anymore.

Why is the range loop in bubble sort reversed?

I am new to Python and learning data structure in Python. I am trying to implement a bubble sort algorithm in python and I did well but I was not getting a correct result. Then I found some tutorial and there I saw that they are first setting a base range for checking.
So the syntax of range in python is:
range([start], stop[, step])
And the bubble sort algorithm is:
def bubbleSort(alist):
for i in range(len(alist) - 1, 0, -1):
for j in range(i):
if alist[j] > alist[j+1]:
temp = alist[j]
alist[j] = alist[j+1]
alist[j+1] = temp
return alist
print(bubbleSort([5, 1, 2, 3, 9, 8, 0]))
I understood all the other logic of the algorithm but I am not able to get why the loop is starting from the end of the list and going till first element of the list:
for i in range(len(alist) - 1, 0, -1):
Why is this traversing the list in reverse? The main purpose of this loop is setting the range condition only so why can't we traverse from the first element to len(list) - 1 like this:
for i in range(0, len(alist) - 1, 1):
In your code, the index i is the largest index that the inner loop will consider when swapping the elements. The way bubble sort works is by swapping sibling elements to move the largest element to the right.
This means that after the first outer iteration (or the first full cycle of the inner loop), the largest element of your list is positioned at the far end of the list. So it’s already in its correct place and does not need to be considered again. That’s why for the next iteration, i is one less to skip the last element and only look at the items 0..len(lst)-1.
Then in the next iteration, the last two elements will be sorted correctly, so it only needs to look at the item 0..len(lst)-2, and so on.
So you want to decrement i since more and more elements at the end of the list will be already in its correct position and don’t need to be looked at any longer. You don’t have to do that; you could also just always have the inner loop go up to the very end but you don’t need to, so you can skip a few iterations by not doing it.
I asked why we are going reverse in the list like len(list)-1,0. Why are we not going forward way like 0,len(list)-1?
I was hoping that the above explanation would already cover that but let’s go into detail. Try adding a print(i, alist) at the end of the outer loop. So you get the result for every iteration of i:
>>> bubbleSort([5, 1, 3, 9, 2, 8, 0])
6 [1, 3, 5, 2, 8, 0, 9]
5 [1, 3, 2, 5, 0, 8, 9]
4 [1, 2, 3, 0, 5, 8, 9]
3 [1, 2, 0, 3, 5, 8, 9]
2 [1, 0, 2, 3, 5, 8, 9]
1 [0, 1, 2, 3, 5, 8, 9]
As you can see, the list will be sorted from the right to the left. This works well for our index i which will limit how far the inner loop will go: For i = 4 for example, we already have 3 sorted elements at the end, so the inner loop will only have to look at the first 4 elements.
Now, let’s try changing the range to go in the other direction. The loop will be for i in range(0, len(alist)). Then we get this result:
>>> bubbleSort([5, 1, 3, 9, 2, 8, 0])
0 [5, 1, 3, 9, 2, 8, 0]
1 [1, 5, 3, 9, 2, 8, 0]
2 [1, 3, 5, 9, 2, 8, 0]
3 [1, 3, 5, 9, 2, 8, 0]
4 [1, 3, 5, 2, 9, 8, 0]
5 [1, 3, 2, 5, 8, 9, 0]
6 [1, 2, 3, 5, 8, 0, 9]
As you can see, this is not sorted at all. But why? i still limits how far the inner loop will go, so at i = 1, the loop will only look at the first pair and sort that; the rest will stay the same. At i = 2, the loop will look at the first two pairs and swap those (once!); the rest will stay the same. And so on. By the time the inner loop can reach the last element (which is only on the final iteration), there aren’t enough iterations left to swap the zero (which also happens to be the smallest element) to the very left.
This is again because bubble sort works by sorting the largest elements to the rightmost side first. So we have to start the algorithm by making the inner loop be able to reach that right side completely. Only when we are certain that those elements are in the right position, we can stop going that far.
There is one way to use a incrementing outer loop: By sorting the smallest elements first. But this also means that we have to start the inner loop on the far right side to make sure that we check all elements as we look for the smallest element. So we really have to make those loops go in the opposite directions.
It's because when you bubble from the start of the list to the end, the final result is that the last item in the list will be sorted (you've bubbled the largest item to the end). As a result, you don't want to include the last item in the list when you do the next bubble (you know it's already in the right place). This means the list you need to sort gets shorter, starting at the end and going down towards the start. In this code, i is always the length of the remaining unsorted list.
You can use this for:
for i in range(0,len(alist)-1,1):
but consequently you should change your second iteration:
for j in range(0,len(alist)-i,1):
I think the purpose of using reverse iteration in the first line is to simplify the second iteration. This is the advantage of using python
as #Jeremy McGibbon's answer, the logic behind bubble sort is to avoid j reach the "sorted part" in the behind of list. When using the example code, j range will be decreased as the value of i decrease. When you change i to increasing, you should handle j iteration differently
You can write the code as follow
lst = [9,6,5,7,8,3,2,1,0,4]
lengthOfArray = len(lst) - 1
for i in range(lengthOfArray):
for j in range(lengthOfArray - i):
if lst[j] > lst[j + 1]:
lst[j], lst[j + 1] = lst[j + 1], lst[j]
print(lst)

Python syntax / understanding

def counting_sort(array, maxval):
"""in-place counting sort"""
m = maxval + 1
count = [0] * m # init with zeros
for a in array:
count[a] += 1 # count occurences
i = 0
for a in range(m): # emit
for c in range(count[a]): # - emit 'count[a]' copies of 'a' #CONFUSED
array[i] = a
i += 1
return array
print counting_sort( [1, 4, 7, 2, 1, 3, 2, 1, 4, 2, 3, 2, 1], 7 )
# prints: [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 7]
So in the code above i dont understand the line I marked with confused, 4 lines before the last.
Might be because I am new to python or just stupid.
What happends in the first case? When the range is [ ] ? ... "for each c in the range of empty array....?
i dont get array[i] = a on the line under aswell. If a is the first element in the counting array which might be zero, how can it be added....? Really confused...
Cheers!
You've apparently figured out that count[a] will be 0, and that range(count[a]) will therefore be [].
So, what you're asking is, what does this do:
for i in []:
do_stuff(i)
The answer is that it loops over each of the 0 elements—in other words, it doesn't loop at all. It just does nothing.*
This is explained in the docs for the for statement:
… The suite is then executed once for each item provided by the iterator… When the items are exhausted (which is immediately when the sequence is empty…) … the loop terminates.
And that implicitly explains your second bit of confusion:
If a is the first element in the counting array which might be zero, how can it be added
When count[a] is 0, you will never get into the loop, so that case cannot ever arise.
* If the for statement has an else clause, it does run the else clause.
range will give you a list with specified start and end values. For example
print range(5)
will print
[0, 1, 2, 3, 4]
When you say, range(m) or range(count[a]) it will generate a list till m or count[a] starting from 0.
About array[i] = a
In the range(m), for each element, the code checks the count[a]. If the count[a] is 0, the second loop will not be executed. (range(0) will produce an empty list) So, for when a is 0 array[i] = a will not be executed.
I simplified part of you code, maybe it it can help you understand the core of the algorithm well . After counting all the elements in the array, we can reconstruct a sorted array only with the information stored in count[].
array=[]
for a in range(m):
array.extend([a]*count[a])

Pop index out of range [duplicate]

This question already has answers here:
Python: pop from empty list
(5 answers)
Closed 3 years ago.
N=8
f,g=4,7
indexList = range(N)
print indexList
print f, g
indexList.pop(f)
indexList.pop(g)
In this code I am getting an error stating that the pop index of g in indexList is out of range.
Here is the output:
[0, 1, 2, 3, 4, 5, 6, 7]
4 7
Traceback (most recent call last):
indexList.pop(g)
IndexError: pop index out of range
I don't understand, g has a value of 7, the list contains 7 values, why is it not able to return me the 7 in the list?
To get the final value of a list pop'ed, you can do it this way:
>>> l=range(8)
>>> l
[0, 1, 2, 3, 4, 5, 6, 7]
>>> l.pop(4) # item at index 4
4
>>> l
[0, 1, 2, 3, 5, 6, 7]
>>> l.pop(-1) # item at end - equivalent to pop()
7
>>> l
[0, 1, 2, 3, 5, 6]
>>> l.pop(-2) # one left of the end
5
>>> l
[0, 1, 2, 3, 6]
>>> l.pop() # always the end item
6
>>> l
[0, 1, 2, 3]
Keep in mind that pop removes the item, and the list changes length after the pop. Use negative numbers to index from the end of a list that may be changing in size, or just use pop() with no arguments for the end item.
Since a pop can produce these errors, you often see them in an exception block:
>>> l=[]
>>> try:
... i=l.pop(5)
... except IndexError:
... print "sorry -- can't pop that"
...
sorry -- can't pop that
After you pop the 4, the list only has 7 values. If you print indexList after your pop(f), it will look like:
[0, 1, 2, 3, 5, 6, 7]
Along with all the other answers, the important part about the pop() function is that it removes the value from the array, thus changing the indexes. After popping index 4, your list is left with 7 items. It is important to know that Python indexes starting at 0 so your 7 item list only contains indexes 0 through 6. That's why popping index 7 is out of bounds, it no longer exists.
Typically a "popping" function is used when implementing a stack or a queue where the goal is to get a value from a list of values waiting to be processed. To avoid processing the same data twice by accident, you make sure to remove it at the same time as retrieval.
Sometimes stacks and queues can be implemented with a peek operation that will return just the value without removing it but since Python implements stacks and queues just using regular arrays without any special wrapper, your peek function would be the standard array[index] call.
----EDIT----
It occurs to me that it could be the case that instead of removing the item at index 7, you wish to remove the value 7. If that's the case, you should call indexList.remove(7). This will remove the first instance of 7 in your list, no matter what its index (and throws an error if there is no value 7). I'm pretty sure you understand that pop() takes an index, though.
Just in case, take a look at the Python datastructures API for more information on what functions are available, what they do, and what arguments they take.

Categories