Can someone explain logic behind this python code - python

This python code is giving certain output someone please explain logic behind it.
l = [1,2,3,4,5]
for l[-1] in l:
print(l[-1])
output for this code is
1
2
3
4
4

You iterate through the list while assigning each value to the last value of list instead of the temporary i that we always use. So you can print every value of list except the last one cause second last one overwrites it and that's why the second last is printed twice.

Now understand for-loop behaviour in python. lets consider the following for loop:
for {val} in l: #curly braces only for the post, please do not write in python
print(val)
What python does is launch an iterator over the list l, and for every value in the list, the variable in the {} is assigned the value and prints it -> 1 2 3 4 5
now what has happened in your code is the {} contain a reference, to the iterating list itself, specifically to -1 index of the list, aka, the last element
so the for-loop still does the exact same thing, goes over the list from left to right, but also assigns the value to the last position of the same list it is iteration over.
DRY RUN:
outside for-loop: l= [1,2,3,4,5]
iteration 1: l= [1,2,3,4,1]
iteration 2: l= [1,2,3,4,2]
iteration 3: l= [1,2,3,4,3]
iteration 4: l= [1,2,3,4,4]
now for last iteration, it is iterating over and assigning to the same position, so it outputs 4 again.
TLDR:
instead of using a temp variable in the for loop, we are using a postion in our list itself, and actually modifying it as a result every iteration.

Related

Getting different results while executing similar "for loops"

Code 1
>>> L=[0,1,2,3]
for i in range(len(L)):
print(f"Counter {i}")
for j in range(len(L)):
print(j)
if len(L)==4:
L.remove(2)
L.remove(3)
else:
pass
[Output] Counter 0
0
1
2
3
Counter 1
0
1
Counter 2
0
1
Counter 3
0
1
Code 2
>>> L=[0,1,2,3]
for i in L:
print(f"Counter {i}")
for j in L:
print(j)
if len(L)==4:
L.remove(2)
L.remove(3)
else:
pass
[Output] Counter 0
0
1
Counter 1
0
1
The two codes are similar but they are giving different results.
In the first code the length of L is 4, so the variable i in first for loop will take values 0,1,2 and 3. For i=0, j again can take 4 values. But in the second loop we make the length of list to 2. So this effect will reflect, when i=1 and so on as can be seen from the output of the code.
But in the second code, after deleting two elements of the list in the second loop, its effect becomes transparent in the next iteration of the second loop.
Why this is so? I am not able to understand whether we use for i in range(len(L)) or for i in L, its effect should be same in the output of 2 codes. In the first code, after deleting two elements of list range(L) does not change immediately, while in the second code we get different outputs.
Can someone explain why this is so?
It's because for the first one, you iterate over a range, meaning you've calculated the number of iterations from the start (4). In the second one, you iterate over a list, meaning it will keep on looping until it runs out of items in the list. Because you remove two items from the list during iteration, it will run out of items two loops sooner.
In the first piece of code, the range(len(L)) is recalculated for each iteration of the outer loop, but that doesn't mean that the outer loop's range(len(L)) will be reevaluated as well.
L=[0,1,2,3]
for i in range(len(L)) -> for i in range(4) for i in L -> for i in [0,1,2,3]
point is while you are using range(len(L)) and than trying to remove from the list it will not affect for loop as it is not dependent on List elements (i.e It is not pointing to the list). only the first time while using range(len(L))->4 now if you remove from the list or even empty the list this will not affect.
but in for i in L you are directly pointing to the list if any thing changes it will affect the for loop also.
see the below image
Image 1 as you can see it is iterating independent of the list.
Image 2 .Dependent on the list

What is the variable "i" in loops? [duplicate]

This question already has answers here:
Scope of python variable in for loop
(10 answers)
Changing iteration variable inside for loop in Python [duplicate]
(6 answers)
Closed 2 years ago.
I saw the following code in a coding challenge, I don't know why it returns 3, but check the code and tell me what do u think.
myList = [1,2,3,4]
for i in myList:
i += 1
print(myList[-2])
When I saw the code I said it will print 4 because in the loop we added 1 in all integers in the list, and [-2] is supposed to give me the second-last value, which is 4 according to what I think.
I know I'm missing something here but I don't know what it is, so if anyone could explain this to me I'll appreciate it. Probably I'm not understanding i, I'm not sure.
Explanation
The line:
for i in myList
Iterates over the items of the list myList, not it's indices.
The line in it that adds one i += 1 doesn't assign the new value to the list, therefore is unchanged.
How to Fix
However, if we change the code to iterate over the indices:
for i in range(len(myList))
We can now change the values in the list:
myList[i] += 1
Code fix:
myList = [1,2,3,4]
for i in range(len(myList)):
myList[i] += 1
# 4
print(myList[-2])
Some Advice
It's a convenience in Python to name variables with lower-case, underscore separated names.
So myList would be my_list.
It doesn't change how the program behaves but would be more readable for your future teammates.
Your reasoning is ok, but note that the changed value is not stored back to the list replacing the original (the list remains unchanged!)
The index of list is
myList = [1, 2, 3, 4]
+ve index 0 1 2 3
-ve index -4 -3 -2 -1
i is modified in that iteration and is not stored in list. Hence myList[-2] is 3
i is the current item of your list, i took the value but not is a reference, therefore is unchanged

swapping elements in a for loop returns the same index after swap

can someone explain me the loop below:
for item in aList[start:end:1]:
aList[aList.index(item)],aList[aList.index(item)+1] = aList[aList.index(item)],aList[aList.index(item)+1]
Let s say aList = [5,2,3,6,1]. the first iteration the index would 0. in the second would be again 0 and after will be increased to 2. Thus it will choose [5,2] and will continue with [2,5] [3,6]....
Why is that and what is the right way to do this?
UPDATE: The above is just an example of a specific behavior I wanted to understand. the actual code tries to implement a coctail sort algorithm.
The actual code is like that
if f == 1:
for d in area[start:end:f]:
print area,f ,d,area[area.index(d)+1],area.index(d)+1
if d > area[area.index(d)+1]:
tmp = area.index(d)
area[tmp], area[tmp+1] = area[tmp+1],area[tmp]
area=area
end -= 1
f = ~f + 1
if f == -1:
for d in area[end:start:f]:
print area,f,d,area[area.index(d)-1],area.index(d)-1
if d < area[area.index(d)-1]:
tmp = area.index(d)
area[tmp], area[tmp-1] = area[tmp-1], area[tmp]
area=area
start += 1
f = ~f + 1
As John said, it is a bad idea to mutate a list whilst looping over it. Instead create a copy of the list and change the data in the copy of the list. However it's also unclear what you are trying to achieve so if you specify that there might be a better way to do it.
Before diving into the problem, it's worth noting there are a couple issues with the code as written.
start and end aren't defined
The assignment in the for-loop doesn't actually swap the order
Those things aside, the algorithm fails, because you iterate over every item. I'll go through 2 iterations to show you why that's so.
Let's use your example:
aList = [5,2,3,6,1]
First iteration:
item = 5
aList.index(5) = 0
so, we're going to swap aList[0] and aList[1]
at the end of the iteration, aList = [2,5,3,6,1]
Second iteration:
item = 2
Wait, why 2? Because the slice operation creates a new list that we're iterating over, and that list has the original order.
aList.index(2) = 0
so, we're going to swap aList[0] and aList[1]... again.
at the end of the iteration, aList = [5,2,3,6,1]... which is the original order.
And so on
On the third iteration, we'll wind up swapping the 3rd and 4th items. And on the fourth iteration of the loop, we'll swap them again.
You can verify this is the case by adding a print statement to your for-loop: print aList.
So how do you fix it?
You can get around this behavior by iterating over every other item, instead of every item. Try changing the slice stride (step) to 2, instead of 1.

Why doesn't the value in for loop change?

Why does the value of range(len(whole)/2) not change after whole is modified? And what do you call range(len...) value in for-loop?
whole = 'selenium'
for i in range(len(whole)/2):
print whole
whole = whole[1:-1]
output:
selenium
eleniu
leni
en
The range() produces a list of integers once. That list is then iterated over by the for loop. It is not re-created each iteration; that'd be very inefficient.
You could use a while loop instead:
i = 0
while i < (len(whole) / 2):
print whole
whole = whole[1:-1]
i += 1
the while condition is re-tested each loop iteration.
The range function creates a list
[0, 1, 2, 3]
And the for loop iterates over the value of the list.
The list is not recreated each and every time
But this is not the case in normal list
wq=[1,2,3]
for i in wq:
if 3 in wq:
wq.remove(3)
print i
1
2

For Loop to While Loop using IN for while loops

I am quite new to Python 2.7 so I had a couple of questions regarding using for loops to while loops.
For example: I am writing this definition
def missingDoor(trapdoor,roomwidth,roomheight,step):
safezone = []
hazardflr = givenSteps(roomwidth,step,True)
safetiles = []
for m in hazardflr:
safetiles.append((m,step))
i = 0
while i < len(safetiles):
nextSafe = safetiles[i]
if knownSafe(roomwidth, roomheight, nextSafe[0], nextSafe[1]):
if trapdoor[nextSafe[0]/roomwidth][nextSafe[0]%roomwidth] is "0":
if nextSafe[0] not in safezone:
safezone.append(nextSafe[0])
for e in givenSteps(roomwidth,nextSafe[0],True):
if knownSafe(roomwidth, roomheight, e, nextSafe[0]):
if trapdoor[e/roomwidth][e%roomwidth] is "0" and (e,nextSafe[0]) not in safetiles:
safetiles.append((e,nextSafe[0]))
i += 1
return sorted(safezone)
I am trying to turn all the for loops to a while loops, so this is currently what I have written so far. I actually dont know if we say "While e in " works near the middle of the code. But using the while loop rules, will this code do the same as the for loop one?
safezone = []
hazardflr = givenSteps(roomwidth,step,True)
safetiles = []
m=0
while m < hazardflr:
safetiles.append((m,step))
i = 0
while i < len(safetiles):
nextSafe = safetiles[i]
if knownSafe(roomwidth, roomheight, nextSafe[0], nextSafe[1]):
if trapdoor[nextSafe[0]/roomwidth][nextSafe[0]%roomwidth] is "0":
if nextSafe[0] not in safezone:
safezone.append(nextSafe[0])
e=0
while e in givenSteps(roomwidth,nextSafe[0],True):
if knownSafe(roomwidth, roomheight, e, nextSafe[0]):
if trapdoor[e/roomwidth][e%roomwidth] is "0" and (e,nextSafe[0]) not in safetiles:
safetiles.append((e,nextSafe[0]))
e+=1
i += 1
m+=1
return sorted(safezone)
thanks for any advice or help!
No, your code isn't identical.
While they look similar, for item in list and while item in list will do wildly different things.
for item in list is a syntactic way of saying for every item in the list - do something with is.
while item in list is different - a while loop iterates as long as the condition is true. The condition in this case being item in list. It doesn't update the item each iteration and if you never change what item or list are, it might never terminate. Additionally, if any given item isn't in the list it may terminate prematurely.
If you want to iterate through a list and keep a count, using while is the wrong way to go about it. Use the enumerate() function instead.
enumerate() takes a list, and returns a list of tuples, with each item from the list in order with its index, like so:
for i,m in enumerate(hazardflr):
safetiles.append((m,step))
This small change means you no longer have to track your indices manually.
If you are iterating through every item in a list in Python - use for that's what it is designed to do.
It depends on exactly what givenSteps returns, but in general, no. for x in foo evaluates foo once and then assigns x to be each element of foo in turn. while x in foo: ... x += 1, on the other hand, evaluates foo on every iteration and will end early if foo is not a contiguous sequence. For example, if foo = [0, 1, 2, 5, 6], for will use every value of foo, but while will end after 2, because 3 is not in foo. while will also differ from for if foo contains any non-integral values or values below the starting value.
while aList:
m= hazardflr.pop()
# ...
should be roughly equivelent to your other loop

Categories