Loop counter behaving unexpectedly - python

I am having trouble understanding why the counter variable even though incremented within
the nested loop, is not having any effect on the number of times the parent loop runs!
The code below gives the output 0 1 2 when the increment inside the nested loop is supposed to stop it from executing the second and the third time.
for i in range(3):
print(i)
for j in range(2):
i = i + 2
Is the i inside the nested for pointing to a different i!

A for loop is a fancy kind of assignment statement; the loop index is assigned to at the start of each iteration.
The code
for i in range(3):
print(i)
for j in range(2):
i = i + 2
is effectively equivalent to
itr1 = iter(range(3))
while True:
try:
i = next(itr1)
except StopIteration:
break
itr2 = iter(range(2))
while True:
try:
j = next(itr2)
except StopIteration:
break
i = i + 2
The assignment i = next(itr1) ignores anything else done to i in the previous iteration: the value depends only on the state of itr1, not the current value of i.

if you display i after the loop incrementing it, you'll see the change
for i in range(3):
print(i)
for _ in range(2): # j is not used
i += 2
print(i) # I'm new!
each time the first line runs, it'll re-tag i with the next value from the range() sequence, discarding what it was before

You don't store the result of the increment anywhere. It goes like this: i is assigned a value from range(), you print it, then j gets the assigned value, i += 1, and then you loop starts again, and i is assigned the next value from the range().
This should work though
for i in range(3):
for j in range(2):
i = i + 2
print(i)

To achieve the effect you are looking for you would have to increment the iterator. You might think of it as incrementing the pointer, rather than the value at the pointer:
iterator = iter(range(123))
for i in iterator:
print(i)
next(iterator, None) #None here is a default,
#and stops you getting an error if there is no 'next'.
Imagine a for loop in python is doing something like the following, which makes it very obvious why a value assigned to i within the body of the loop is lost. You are incrementing i when you really want to increment n
iterator = list(range(10))
n = 0
while n < len(iterator):
n+=1
i = iterator[n]
print(i)
i = i+1

Related

How can I control the iterator value inside a for loop?

I would like to reproduce this JavaScript bucle in Python, but I'm not sure how to proceed.
for (i=0;i<toks.length && toks[i]!='\r'; i+=10)
you can write it using while
i = 0
while(i < len(toks) and toks[i] != '\r'):
i += 10
Personally, I don't like looping with indexes, so I suggest python for loop with break state (which stops the loop). Regarding every 10th loop logic, you can have this using toks[::10].
So the final code would be:
for v in toks[::10]:
if v == '\r':
break
print(v)
You can configure increment with 3rd argument of range and then check exit condition within the loop
toks = list(range(100))
toks[30] = '\r'
for i in range(0, len(toks), 10):
if toks[i] == '\r':
break
print(toks[i])
If you want to add 10 to the iterator variable for each loop, you can simply do so by passing it as a parameter to the range function:
for i in range(0, MAX_ITERATIONS, 10):
pass
The range function takes 3 parameters: the value to initialize the iterator variable, the maximum iterations and the value to add to the iterator variable for each loop (default is 1).
A code like that in python would be something like:
for i in range(0, len(toks), 10):
if toks[i] == '\r':
break

The counter in the for loop doesn't get change

when I put i=x in the given code, the i in the for loop doesn't get change.
s=input()
d={}
Str=""
for i in range(len(s)):
if s[i] not in d:
d[s[i]]=i
else:
x=d[s[i]]
res=str("".join(d.keys()))
if len(Str)<len(res):
Str=res
i=x
d.clear()
print(Str)
The counter, i, does get changed to x, but only for a brief instance. Think of for i in range(n) being implemented like:
i = first value in range
while True:
do something;
i=x; # your assignment
if no more values in range:
break;
else:
i = next value in range;
So right after you assign x to i, you ultimately reach the bottom of the for loop and assign to i the next value in the range expression.
The above is not exactly how range is implemented, but you get the idea. You are expecting it to behave more like:
i = 0
while i < n:
do something;
i = x;
i += 1
You could implement your loop that way.
i is the counter, so if you set it to a value inside the loop it's not going to count properly.

How can I skip specific iterations in a for loop?

Basically, I have a nested for loop. In the inner loop, something happens, and I can skip 3,4,5 or however many iterations i need skipped. But I can't do the same for the outer loop.
Hope that made sense.
Here is my code:
phraseArray = []
phraseArray2 = []
counterAdd = 0
counter = 0
try:
for i in range(len(wordArray)):
for j in range(len(wordArray2)):
if wordArray[i]==wordArray2[j]:
counter = 0
counter2=3
while True:
if wordArray[i+counter]==wordArray2[j+counter]:
counter = counter+1
if counter==3:
phraseArray.append(wordArray[i+0])
phraseArray.append(wordArray[i+1])
phraseArray.append(wordArray[i+2])
elif counter>3:
phraseArray.append(wordArray[i+counter2])
counter2 = counter2+1
else:
phraseArray.append(" ")
j=j+counter
break
except IndexError:
print phraseArray2
The j = j+1 is used to skip certain iterations. I cant do the same for the outer loop because the inner loop changes the counter variable which dictates how many iterations need to skipped. Any suggestions?
Thanks in advance guys! :)
I'd work with iterators here.
import itertools
def skip(iterable, n):
next(itertools.islice(iterable, n, n), None)
outer_numbers = iter(range(...))
for i in outer_numbers:
inner_numbers = iter(range(...))
for j in inner_numbers:
if condition:
skip(outer_numbers, 3) # skip 3 items from the outer loop.
skip(inner_numbers, 2) # skip 2 items from the inner loop.
Of course, you may want/need continue and/or break statements.
A general form for skipping multiple iterations of a loop could work like this.
skips = 0
for x in y:
if skips:
skips -= 1
continue
#do your stuff
#maybe set skips = something
You can't use "break" in the outer loop because this will finish the loop and not skip it, what you can do is use some IF statements to control the cases you want. something like
if(condition=skip):
#do nothing
else:
# do

How to change index of a for loop?

Suppose I have a for loop:
for i in range(1,10):
if i is 5:
i = 7
I want to change i if it meets certain condition. I tried this but didn't work.
How do I go about it?
For your particular example, this will work:
for i in range(1, 10):
if i in (5, 6):
continue
However, you would probably be better off with a while loop:
i = 1
while i < 10:
if i == 5:
i = 7
# other code
i += 1
A for loop assigns a variable (in this case i) to the next element in the list/iterable at the start of each iteration. This means that no matter what you do inside the loop, i will become the next element. The while loop has no such restriction.
A little more background on why the loop in the question does not work as expected.
A loop
for i in iterable:
# some code with i
is basically a shorthand for
iterator = iter(iterable)
while True:
try:
i = next(iterator)
except StopIteration:
break
# some code with i
So the for loop extracts values from an iterator constructed from the iterable one by one and automatically recognizes when that iterator is exhausted and stops.
As you can see, in each iteration of the while loop i is reassigned, therefore the value of i will be overridden regardless of any other reassignments you issue in the # some code with i part.
For this reason, for loops in Python are not suited for permanent changes to the loop variable and you should resort to a while loop instead, as has already been demonstrated in Volatility's answer.
This concept is not unusual in the C world, but should be avoided if possible.
Nonetheless, this is how I implemented it, in a way that I felt was clear what was happening. Then you can put your logic for skipping forward in the index anywhere inside the loop, and a reader will know to pay attention to the skip variable, whereas embedding an i=7 somewhere deep can easily be missed:
skip = 0
for i in range(1,10):
if skip:
skip -= 1
continue
if i=5:
skip = 2
<other stuff>
Simple idea is that i takes a value after every iteration irregardless of what it is assigned to inside the loop because the loop increments the iterating variable at the end of the iteration and since the value of i is declared inside the loop, it is simply overwritten. You'd probably wanna assign i to another variable and alter it. For e.g,
for i in range(1,10):
if i == 5:
u = 7
and then you can proceed to break the loop using 'break' inside the loop to prevent further iteration since it met the required condition.
Just as timgeb explained, the index you used was assigned a new value at the beginning of the for loop each time, the way that I found to work is to use another index.
For example, this is your original code:
for i in range(1,10):
if i is 5:
i = 7
you can use this one instead:
i = 1
j = i
for i in range(1,10):
i = j
j += 1
if i == 5:
j = 7
also, if you are modifying elements in a list in the for loop, you might also need to update the range to range(len(list)) at the end of each loop if you added or removed elements inside it. The way I do it is like, assigning another index to keep track of it.
list1 = [5,10,15,20,25,30,35,40,45,50]
i = 0
j = i
k = range(len(list1))
for i in k:
i = j
j += 1
if i == 5:
j = 7
if list1[i] == 20:
list1.append(int(100))
# suppose you remove or add some elements in the list at here,
# so now the length of the list has changed
k = range(len(list1))
# we use the range function to update the length of the list again at here
# and save it in the variable k
But well, it would still be more convenient to just use the while loop instead.
Anyway, I hope this helps.

python: restarting a loop

i have:
for i in range(2,n):
if(something):
do something
else:
do something else
i = 2 **restart the loop
But that doesn't seem to work. Is there a way to restart that loop?
Thanks
You may want to consider using a different type of loop where that logic is applicable, because it is the most obvious answer.
perhaps a:
i=2
while i < n:
if something:
do something
i += 1
else:
do something else
i = 2 #restart the loop
Changing the index variable i from within the loop is unlikely to do what you expect. You may need to use a while loop instead, and control the incrementing of the loop variable yourself. Each time around the for loop, i is reassigned with the next value from range(). So something like:
i = 2
while i < n:
if(something):
do something
else:
do something else
i = 2 # restart the loop
continue
i += 1
In my example, the continue statement jumps back up to the top of the loop, skipping the i += 1 statement for that iteration. Otherwise, i is incremented as you would expect (same as the for loop).
Here is an example using a generator's send() method:
def restartable(seq):
while True:
for item in seq:
restart = yield item
if restart:
break
else:
raise StopIteration
Example Usage:
x = [1, 2, 3, 4, 5]
total = 0
r = restartable(x)
for item in r:
if item == 5 and total < 100:
total += r.send(True)
else:
total += item
Just wanted to post an alternative which might be more genearally usable. Most of the existing solutions use a loop index to avoid this. But you don't have to use an index - the key here is that unlike a for loop, where the loop variable is hidden, the loop variable is exposed.
You can do very similar things with iterators/generators:
x = [1,2,3,4,5,6]
xi = iter(x)
ival = xi.next()
while not exit_condition(ival):
# Do some ival stuff
if ival == 4:
xi = iter(x)
ival = xi.next()
It's not as clean, but still retains the ability to write to the loop iterator itself.
Usually, when you think you want to do this, your algorithm is wrong, and you should rewrite it more cleanly. Probably what you really want to do is use a generator/coroutine instead. But it is at least possible.
a = ['1', '2', '3']
ls = []
count = False
while ls != a :
print(a[count])
if a[count] != a[-1] :
count = count + 1
else :
count = False
Restart while loop.

Categories

Resources