
I always teach my kids about sentinel loops. I have them avoid breaking loops at all costs! What about do loop untils? -- Sent from my Android phone with K-9 Mail. Please excuse my brevity. John Zelle <john.zelle@wartburg.edu> wrote: Oops, I think I have an off by one error in my last example. I would change the conditional at the bottom to: if guess == secret: print("You guessed it!") else: print("You maxed out.") That's clearer anyway. John Zelle, PhD Professor of Computer Science Wartburg College _____________________________________________ From: edu-sig-bounces+john.zelle=wartburg.edu@python.org [edu-sig-bounces+john.zelle=wartburg.edu@python.org] on behalf of John Zelle [john.zelle@wartburg.edu] Sent: Saturday, April 21, 2012 8:11 AM To: kirby urner; edu-sig@python.org Subject: Re: [Edu-sig] a short essay about programming Kirby, There are some nice thoughts here that I don't really disagree with, but your code examples don't use the while conditions well. If you put a condition on the loop, there should be no reason to retest the same condition inside the loop. Think of the loop condition as a guard, inside the loop it is true, outside the loop it has become false. That suggests the more elegant (in my eyes) way to write your first example as a sort of sentinel loop: guess = int(input("Guess? ")) while(guess != secret): // as long as the user didn't get it, get another guess print("Nope, try again") guess = int(input("Guess? ")) // Here we know the condition is false print("You guessed it") There's no reason for the re-test of the loop condition to either break or continue. This applies to the second example as well, but a post-loop conditional will still be required to figure out why the loop quit: allowed = 5 guess = int(input("Guess? ")) tries = 1 while guess != secret and tries < allowed: //user gets to try again print("Nope, try again") guess = int(input("Guess? ")) tries += 1 if tries <= allowed: print("You guessed it") else: print("You've maxed out.") I like having the loop condition telling us exactly what the loop accomplishes. Using something like an "exit" or "done" variable obscures that because it does not announce what is required in order for the loop to be done. Of course the cost of this style is the repeated input statement, but a priming read is a standard part of a sentinel loop, and both examples are shorter than the versions that retest or assign a conditional inside the loop. John Zelle, PhD Professor of Computer Science Wartburg College _____________________________________________ From: edu-sig-bounces+john.zelle=wartburg.edu@python.org [edu-sig-bounces+john.zelle=wartburg.edu@python.org] on behalf of kirby urner [kirby.urner@gmail.com] Sent: Saturday, April 21, 2012 2:45 AM To: edu-sig@python.org Subject: [Edu-sig] a short essay about programming A common "error" (not too serious) that I see in beginning Python (and no doubt other languages, but Python is the one I'm teaching), is having a while condition that appears to put a lid on things, but then the flow all leaks away through break statements, such that the "front door" condition is never revisited. while guess != secret: guess = int(input("Guess?: ") if guess == secret: print("You guessed it!") break else: print("Nope, try again...") What's messed up about the above code is you never really go back to the top in the case where you'd get to leave. Rather, you exit by the back door, through break. So in that case, wouldn't have been simpler and more elegant, even more "correct" (dare I say it) to have gone: while True: # no ifs ands or buts guess = int(input("Guess?: ") if guess == secret: print("You guessed it!") break else: print("Nope, try again...") I see lots of heads nodding, and that could be the end of the matter, but then a next question arises: wouldn't this also be a more correct solution?: while guess != secret: guess = int(input("Guess?: ") if guess == secret: print("You guessed it!") continue # instead of break else: print("Nope, try again...") We're back to having a variable while condition, not a boolean constant, but this time we actually exit by means of it, thanks to continue or... while guess != secret: guess = int(input("Guess?: ") if guess == secret: print("You guessed it!") else: print("Nope, try again...") ... thanks to no continue. This last one is getting a thumbs up, but then I'd pause here and say "continue" can be easier on the eyes. It's unambiguous where it takes you, in contrast to having to scan on down the gauntlet, looking for possibly other open doors. "What happens next" should not require scanning ahead too far. Help us not to get lost. Be a civic-minded coder. I'm thinking of a programming style that advises two things: (a) if you use a while condition that's variable, that's expected to change, then your goal should be to always exit because of that, i.e. that should be your only exit point. Even if some other criterion suggests exiting, you have the option to flip that "lid" at the top, to crack that front door, and bounce the ball out. (b) which is why 'continue' is your friend. You are showing the user where your 'break' statements are, except you don't use "break" statements, as you've given a non-constant condition, and your aim is to make that your ONLY exit point. In short: never use break to exit a while loop unless your condition is while True. Instead, always flip the condition and exit through the font door. However, as soon as I make that rule I can think of good reasons to break it. The other programmers in the room are shaking their heads. Won't we lose information, needed elsewhere in the program, if we "artificially" force a True condition to False. Aren't we, in effect, lying? That concern could be addressed. Keep all the info true, just treat the "lid condition" (it "keeps a lid on it") as a flag. Go ahead and remember the user's guess. allowed = 5 tries = 1 exit = False while not exit: # no other way out guess = int(input("Guess?: ") if guess == secret: print("You guessed it!") exit = True # instead of break continue print("Nope, try again...") tries += 1 if tries == allowed: print("You've maxed out") exit = True continue I think we all understand the main issue: writing reader-friendly code, and rules for doing so. There's something comforting about approaching a while loop and knowing its not a leaky sieve, riddled with back doors, maybe even an exit( ) time bomb. But in the recursion world we want a minimum of two exits usually: when another round is called for versus when we've "hit bottom". Can we have it both ways? Conclusions: Lets not be too hasty with rules of thumb and: Lets keep the reader in mind when writing code. Just because the interpreter knows to compute the flow unambiguously, doesn't mean all ways of writing it are equally reader-friendly. What may seem a gratuitous gesture, an unnecessary flourish, may actually promote reader comprehension of your code, and that should be a goal as much as satisfying the interpreter. Kirby _____________________________________________ Edu-sig mailing list Edu-sig@python.org http://mail.python.org/mailman/listinfo/edu-sig _____________________________________________ Edu-sig mailing list Edu-sig@python.org http://mail.python.org/mailman/listinfo/edu-sig _____________________________________________ Edu-sig mailing list Edu-sig@python.org http://mail.python.org/mailman/listinfo/edu-sig