statements in control structures (Re: Conditional Expressions don't solve the problem)

Huaiyu Zhu huaiyu at gauss.almadan.ibm.com
Wed Oct 24 21:17:31 EDT 2001


On Wed, 24 Oct 2001 20:11:31 GMT, William Tanksley
<wtanksle at dolphin.openprojects.net> wrote: 
>
>I'm confused -- I don't see how your version can work either.  Both of you
>are iterating over an empty list, and the only code which could possibly
>add anything to the list is inside the iteration; that list is empty no
>matter what, so nothing will execute, and the list will never be anything
>but empty.

That's what that 'else' is for - and that's the whole point of this thread.
I have a suspicion that the usage of for-else in Python is not well known to
many discussants in this thread.

The equivalent code is (note the duplication of primes.append(n)):
    
    def getprimes(x):
        primes = []
        for n in range(2, x):
            for p in primes:
                if p*p > n:         primes.append(n); break
                elif n % p == 0:    break
            else:           primes.append(n)
        return primes

Try it and see how it works.
    

>I don't like your "for x in list while y" construct, either.  It's
>terrificly ambiguous: does it mean that every item in the list which meets
>the 'y' criterion will be processed, or does it loop over all the items up
>to the first one which fails to satisfy 'y'?

So let me explain this in detail and hope I don't need to do it again.  The
pseudo code was:
    
    def getprimes(x):
        primes = []
        for n in range(2, x):
            for p in primes while p*p < n:
                if n % p == 0: break
            else: primes.append(n)
        return primes
    
The 'for p' loop will exit under three conditions:
- p reaches end of primes: jump to (A)
- not p*p < n: jump to (A)
- n % p == 0: jump to (B)

where 

(A) primes.append(n)
(B) pass


The juicy part of the code is:
    
            for p in primes while p*p < n:
                if n % p == 0: break
            else: primes.append(n)

Here is a translation into as much verbage as possible: loop p through
primes, until either the end of primes, or p*p is too large, at that point
we consider the loop as exhausted.  If in the middle of the loop we found p
is a factor of n, then n is not a prime.  End of the story.  However, if we
have exhausted the loop without finding a factor, then p is a prime, and it
should be recorded in the list of primes.

Note that the two different conditions corresponding to (A) are both
considered intuitively as "exhausted all the candidate factors".

So the question is: is there a way to write the idea that two of the exits
go to the same follow-up code (A) without actually duplicating the code in
(A), or using a temporary variable to hold the state information?  I have
not found a positive answer (which is not the same as claiming it does not
exist).

Why does this matter at all?

To generalize this example, many loops appears to have these two
semantically different exits

- normal exits (like (A) above): intuitively it means the end of the loop is
  reached, or all possible candidates are exhausted.

- abnormal exits (like (B) above): intuitively it means some specific
  condition is met inside the loop so there is no need to examine remaining
  candidates. 

In Python, the abnormal exits are always spelt as 
   if condition:
       break

Therefore, if there is any code that's needed at this point, it can be done
with

    if condition:
        do_something
        break

On the other hand, The Python structures while-else and for-else are
designed to handle the normal exits.

    for ...:
        ...
    else:
        do things required at normal exit

Ditto for the while-loop.

Therefore, if we change both exit conditions into an if-break, the
differences will be lost, and we cannot use the "else" clause.  When Andrew
translated the pseudo code mechanically, he changed the "while" condition
into if-break and moved the code in the "else" there, but it did not help
that the original "else" also takes care of the exits due to the end of the
for-loop itself.

I hope this clarifies what I meant when I said that the issue really appears
to be the existence of two types of exits of loops.

Of course, all of these could be easily done by using an auxiliary variable
found_factor which is set to true or false before different breaks, and
using an "if found_factor" after the loop.  But I found the pseudocode
clearer, as long as one makes the mental distinction between "exiting due to
exhausting of candidates" and "exiting due to a special condition being
met".  It would be convenient if Python allows such pseudocodes to be real
codes.

Is this convenience worth a syntax change?  It really depends on how much
the change is needed.  Considering all the issues discussed, I have not
found a change that has a high enough benefit/cost ratio, so I am not
pushing for any of them now.

In any case, a syntax that removes the benefit of existing for-else and
while-else structures would look quite a retrogress to me.


>>Does the above count as an example?  Can you see that the problem with the
>>simple mechanical translation is that it does not handle properly the two
>>different kinds of exits?
>
>>Hint: There are three exits in the 'for p' loop.  Two of them should lead to
>>the same follow-up code, the other leading to a different follow-up code.
>>Finding them out is left as an exercise to the reader.  :-)
>
>I don't see any followup code in any case -- the loop is simple and
>direct.  You entire function is one big loop which runs /n/ times, but
>inside that is another loop which runs 0 times.  There's no code outside
>of the loops.

I hope you can see it now.  Anyway, I don't think I can do it any better
than this, so I'll just leave it here.


Huaiyu



More information about the Python-list mailing list