Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
For-else loop misbehavior?
#1
REF: The Python Tutorial, 4.4. break and continue Statements, and else Clauses on Loops, 1st example code.

The example looks straight forward (no pun intended) until you insert code between the inner 'for' and 'if'.

To watch the variable values during execution, I added the debugging line:

print('n=' + str(n) + ', x=' + str(x))'

for n in range(2, 10):
    for x in range(2, n):
        print('n=' + str(n) + ', x=' + str(x))      
        if n % x == 0:
            print(n, 'equals', x, '*', n//x)
            break
    else:
        # loop fell through without finding a factor
        print(n, 'is a prime number')


Where's the output from, "print('n=' + str(n) + ', x=' + str(x)) ", before "2 is a prime number" ?!?!

Change the start value of 'for n in range(2,10)'

to a non-prime, i.e. 'for n in range(4,10)'

and now the 'print' line before the 'if' test works as expected.

The line to display the variable values should execute before the 'if' test in every iteration of the loop.

This illogical behavior ONLY occurs the very first time through the inner loop. Each successive time, the code executes as expected.

Never seen a 'for' loop behave like this in any other language. Is the 'else' clause somehow to blame?
Reply
#2
else part in the for loop is executed only if no break statement was executed.
So inner loop x will iterate over all integers from 2 to n-1, if n is divisible by x, break statement will be executed thus else part will not be executed.
in case n is prime, break statement will not be executed and so once inner loop finish it will executed also the else part.
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#3
Please try the sample code.

With 'for n in range(2,10)'
First line of output should always be: n=2, x=2 (this is never output!)

Again with 'for n in range(4,10)' (or any non-prime number as range start)
Output works as expected now! n=4, x=2

SAMPLE CODE:
for n in range(2, 10):
    for x in range(2, n):
        # ignored 1st-time through inner loop
        #  when n range starts on prime number 
        print('n=' + str(n) + ', x=' + str(x))
        if n % x == 0:
            print(n, 'equals', x, '*', n//x)
            break
    else:
        # loop fell through without finding a factor
        print(n, 'is a prime number')
Reply
#4
(Sep-11-2018, 07:55 PM)Johne1 Wrote: First line of output should always be: n=2, x=2 (this is never output!)
if n=2 then in the inner loop x will try to iterate over range(2,2). However range(2,2) is empty one:
>>> list(range(2,2))
[]
so the loop body (lines 3-8) is never executed (and thus no break statement) so else part (lines 10-11) is executed and it prints that 2 is prime number
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#5
>>> list(range(4,2))
[]
That list is empty, too, yet the inner loop hits my first print function
when I change outer loop to 'n in range(4,2)'.
Reply
#6
no, when n=4, the inner loop is
for x in range(2, 4):
i.e. the range is not empty
>>> list(range(2,4))
[2, 3]
so x will first be equal to 2. It will execute the print function on line 5, then the if condition (on line 6) will be evaluated to True, it will execute the print function on line 7, then execute break on line 8. Because the break was executed, the else part will not be executed
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#7
Thanks! I'm going to have to read up on the range() and list() functions to understand why range(2,2) is empty. I'm baffled.
Reply
#8
(Sep-11-2018, 09:00 PM)Johne1 Wrote: why range(2,2) is empty
range(start, stop, step) will give you the numbers from start to stop-1, i.e. stop is not included. start and step are optional,i.e.
range(n) is same as range(0, n, 1)

so range(2,2) start=2 and stop =2, that means it starts from 2 and should stop at 2-1, i.e. 1.
In other words, upper bound (1) is smaller than the lower bound (2).

Or if you prefer, range (assuming step is 1), is the intersection of the set of all integers greater or equal to start (i.e. greater or equal to 2) and the set of all numbers lower than stop (in this case lower than 2). The intersection is empty set.

list function in this case is only for presentation purpose. i.e. I use it just to show what range object is. in python3 range returns range object and if you print it, you will not get what numbers are included:
>>> range(2,4)
range(2, 4)
>>> list(range(2,4))
[2, 3]
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#9
Thanks, again! Great, simple explanation. I was over-thinking it, confusing it with a 2-dimensional array, instead of a simple list.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020