[Edu-sig] a short essay about programming

david at handysoftware.com david at handysoftware.com
Mon Apr 23 00:06:47 CEST 2012

Yes, interesting thread. I've been thinking about this statement:

"If the loop condition is C and there are no breaks, then you know after the loop that not C must be the case."

What if condition C includes an expression like (datetime.datetime.now().hour) == x)?

What if condition C is (not q.empty()), where q is a queue.Queue object fed by other threads?

I think there has to be some constraints on C for that statement to be correct. Something like "and C does not refer to volatile attributes nor variables and doesn't contain method calls whose results vary over time" or something like that.

I guess that's why I think using "while True:" with "break" is Ok in many cases.

David H

-----Original Message-----
From: "John Zelle" <john.zelle at wartburg.edu>
Sent: Saturday, April 21, 2012 11:13pm
To: "kirby urner" <kirby.urner at gmail.com>, "edu-sig at python.org" <edu-sig at python.org>
Subject: Re: [Edu-sig] a short essay about programming

Hi All,

Interesting thread. Sorry about the extraneous parentheses in my initial post, I've just come off a semester of Java and am reflexively putting parentheses around my conditions.

One last thought. While I sometimes use the infinite loop form (while True:) I think the explicit loop condition is almost always preferable because of its value in clarifying what the loop is about and simplifying reasoning about the code. If the loop condition is C and there are no breaks, then you know after the loop that not C must be the case. When the loop condition is True (or a Boolean flag) or the loop contains breaks, you must mentally execute the loop in order to know the state of the computation immediately following.

Of course, the real bonus is when the explicit condition, C, is accompanied by a loop invariant, INV,  that describes what the loop is actually trying to accomplish. Together not C and INV should be sufficient to convince yourself (and others reading your code) that your loop has accomplished exactly what you wanted it to.


John Zelle, PhD
Professor of Computer Science
Wartburg College

From: edu-sig-bounces+john.zelle=wartburg.edu at python.org [edu-sig-bounces+john.zelle=wartburg.edu at python.org] on behalf of kirby urner [kirby.urner at gmail.com]
Sent: Saturday, April 21, 2012 9:12 PM
To: edu-sig at python.org
Subject: Re: [Edu-sig] a short essay about programming

... another useful contribution to this thread.

---------- Forwarded message ----------
From: Richard Pattis <pattis at ics.uci.edu>
Date: Sat, Apr 21, 2012 at 6:53 PM
Subject: I'm not allowed to post
To: kirby urner <kirby.urner at gmail.com>

Feel free to post this for me, which got returned. Probably
because I use a mail alias and never tried to post before.


I replied to Kirby privately, and he suggested I post to the listserv,
so I have. Because UCI is starting to teach Python next year, I might
be on the listserv (more than you want) over the summer as I learn
Python and how to program in it and teach it: a tall order for a few
months. As Charles McCabe (SF Examiner) said, "Any clod can
have the facts; having opinions is an art." - rep


I'm a long time Java teacher (heck, I started teaching Fortran in
1976) who will soon be teaching Python. We (here at UC Irvine)
are switching to a 3 quarter introduction to programming sequence
all in Python (and then on to Java and C++ for upper division courses,
although Python could leak upwards).

I've been subscribing to edu-sig Python for a while and appreciate
your efforts (and have learned from them).

I'm a big fan in Java of teaching beginners only
for(;;) -I call it for-ever- and if/break termination (although some
colleagues accuse me of teaching machine language in Java).

I like forever if/break for a few reasons, among them are I
teach ifs right before looping, so students get a chance to
immediately use ifs; I think it is easier to state the
condition in which to break instead of the condition in which
to continue (although we discuss how to compute one from
the other by DeMorgan's laws later); there is only one looping
form, so students aren't faced with the up-front decision of what
kind of loop to use; and finally, they can fully write the loop and
its body and then worry about termination later (unlike a while
loop, where syntactically the first thing you must write is the
continuation test -although some computer scientists would
consider this a feature, not a bug).

So my students don't have to think of which Java loop to use:
while or do/while. They code everything (early in class) using
forever if/break, although later I illustrate the semantics of Java's
other looping forms, using forever if/break (which they are familiar
with), tell them if the pattern fits, do the transformation to simplify
their code, and if you do this enough, later in the course you will
write the "right" pattern first, without thinking about the transformation.

I'm not a big fan of continue here. I conceptualize this as an
N=1/2 loop (sentinel loops are also of this form) and am happy
with forever if/break; The question I'd have for a while loop, is
where does guess come from the first time the test is evaluated;
if the answer is some initialization that is not equal to secret, I'd
say yuck: guess should store the user's guesses only, not some crazy
value to make things work the first time the loop is entered.

Another reason I don't like continue is that it isn't local; you have to
"go around the loop again to exit".; and will you exit the next time
around, it doesn't jump out at me (same, but a little less so, for your
exit = True code in the later example; more soon). With break, you recognize
termination and perform the action immediately (like return). Also,
you have to have the condition stated twice in your final code, in two
opposite forms (guess != secret) and also (guess == secret); confusing
and double yuck.

Now in Java you could write this as a do/while,but you still have the
"repeat the test in two opposite forms" problem.

In your summary

> In short:  never use break to exit a while loop
> unless your condition is while True.

I agree with this, and argue this should be your loop form always
for beginners. I also require (for most of the quarter) at most one
break per loop.

> However, as soon as I make that rule I can
> think of good reasons to break it.

Yes, I believe you should break that rule, not continue to use  it

In the next code you set exit = True then continue so the next
iteration terminates
the loop, which I think is also bad because of non-locality. You are
terminating indirectly all over the loop. I tell my students since
loop termination
is what the loop is all about, it should be obvious where you are
terminating and why. I start by making them put their break statements in
comment sandwiches, so they stand out:


OK, so how would I write your loop to match its semantics, but be
as simple as possible?  Before showing my code, I dislike the fact
that your code is impolite: it always says "try again" on a wrong guess,
but then sometimes doesn't let the user try again. So I'll fix this "feature".

In fact, your code infinite loops when allowed = 1 (because tries,
which is initialized
at 1, is incremented before it is tested == allowed); in other cases
it is impolite
and gives the user one fewer try than they should get; so I'll fix
those bugs (you
could fix them by setting tries to 0, which I think is right, because
the user hasn't
tried yet; I'd increment tries right before or after the user enters a
new guess: more

Moral, never post code to the internet: the bas**rds will tear you apart).
Here is my Java in Python code. I'm assuming that the user always gets
at least one guess.

allowed = 5
tries   = 0

while true:
   guess = int(input("Guess?: ")
   tries += 1

   if guess == secret:
       print("You guessed it!")
       break;                                        # terminate loop

   if tries == allowed:
       print("You've maxed out")
       break;                                        # terminate loop

   print("Nope, try again...")

one fewer variable (no exit; Sartre had good programming intuition)
two fewer (18%) statements in the loop body. Now, we can still do some things to
reduce the size (but possibly increase the complexity of understanding
this code, and requiring knowledge of an "advance" python feature)

while true:
  guess = int(input("Guess?: ")
  tries += 1
  if guess == secret or tries == allowed
      break                                          # terminate loop

  print("Nope, try again...")
print("You guessed it" if guess == secret else "You've maxed out")

Note the last statement cannot be if tries != allowed, because both
might be true,
but the secret test does what you want.

What I like about this solution is it localizes the termination test (one
break) and the final disposition of the game (one "you won/lost"
statement). What I dislike about it (I'm no Pygmalion) is the uncoupling
between the termination and printing, and it has "redundant " tests
(now three, instead of two: one of my big complaints in the code you
started this discussion with).

I admit, I didn't follow any "simple to state" rules when writing this code.
My rule of thumb is not to minimize the number of loop exits but to
minimize the complexity, and I'm not totally sure what that means.

Feel free to rip me for any mistakes I've made, but now I'm off to think about
"private" variables in Python classes.

Rich Pattis

[ trimmed off copy of Kirby's essay -- Kirby ]
Edu-sig mailing list
Edu-sig at python.org
Edu-sig mailing list
Edu-sig at python.org

More information about the Edu-sig mailing list