The counter in the for loop doesn't get change - python

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.

Related

Loop counter behaving unexpectedly

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

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

PYTHON3: indentation of return values

This is working fine but I just don't get it why this works in this way. I think the return of True value should be inside the for loop but when I run this program it works in the opposite way.
Can someone point out what i am misunderstanding about the indentation of return values?
Even though the solution was even shorter I wanted to know exactly about my way of coding. Please help!
# My attempt
def palindrome(s):
mylist = list(s)
j = -1
for i in range(0,len(mylist)-1):
if mylist[i] == mylist[j]:
i+=1
j-=1
continue
return False
return True
# Solution answer:
def palindrome(s):
return s == s[::-1]
When a function is called, the function can return only once.
This kind of return pattern is very frequently found across various programming languages. It is intuitive and efficient.
Let's say you have to check if a list of 1000 values contain only even numbers. You loop through the list and check if each element is even. Once you find an odd number, you do not need to go further. So you efficiently and immediately return and exit from the loop.
Here is hopefully a little bit more intuitive version of your code:
def palindrome(s):
l, r = -1, 0 # left, right
for _ in range(0, len(s) // 2 + 1): # more efficient
l += 1
r -= 1
if s[l] != s[r]:
return False
return True
Once you know the input is not palindrome, you do not need to go further.
If you did not need to stop, it is palindrome.
They follow the exact same rules as any other statement. What you have written means
def palindrome(s) {
mylist = list(s)
j = -1
for i in range(0,len(mylist)-1) {
if mylist[i] == mylist[j] {
i+=1
j-=1
continue
}
return False
}
return True
}
# My attempt
def palindrome(s):
mylist = list(s)
j = -1
for i in range(0,len(mylist)-1):
if mylist[i] == mylist[j]:
i+=1
j-=1
continue
return False
return True
In the above code what happens is inside the for loop each time it checks if there is a mismatch in the values by comparing values by iterating over the list forwards using variable "i" and backwards using variable "j". and returns false immediately if any one letter mismatches and so exits from the loop. And true is returned only once the for loop is completed which means no mismatch was found in the loop
Note: i=0 gives first index, i+=1 iterates forward and j=-1 gives last index, j-=1 iterates backward
Basically, when you index an array in numpy, you do it the way:
a[start:end:step]
,for every dimension. If step is negative, you return the values in inverse order. So, if step is -1, the array a[::-1] is the inverted array of a[::].
a[::-1] = a[::]
Then, if a sequence is the same as its inverse, by definition, it is a palindrome.
See:
https://www.geeksforgeeks.org/numpy-indexing/

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