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
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
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
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
On Sat, Apr 21, 2012 at 6:11 AM, John Zelle <john.zelle@wartburg.edu> wrote:
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.
Ah now there's a good rule of thumb maybe. Whatever sentinel condition let you into the loop should not be retested inside the loop. That sounds cogent. A related but somewhat contrary mental picture is: only let live fish into the fish tank, but some die once already inside the tank. Are there cases where you need a preview, aren't ready to exit yet, so need to re-test inside?
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
Pausing here: I see this style quite often, of parens crammed right up against the while keyword. I 'm always worried when I see that in beginner Python that they're imagining while is a callable, and their feeding it some kind of argument. Same with if: I'll get if(x>5): You're an experienced Pythonista though...
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.
Or is there sometimes? I'm thinking this is an excellent rule of thumb of the kind that needs to be broken. Python in particular is interesting because it tries to stay simple - economical in providing control over flow. Other languages gorge themselves on all imaginable kinds of loop syntax, bulking up their keyword vocabulary in the process sometimes (e.g. 'until'). I agree your examples look more elegant and economical than mine featuring a re-test.
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.")
This is where the while statement's option else suite proves useful, if you want to execute a block precisely because the while condition has flipped. I tend to find this useful in conjunction with 'break' though, exploding my earlier hypothesis that while loops should have only one entrance and one exit. while guess != secret: # front door if tries > allowed: print("Bummer") break # back door tries += 1 guess = input ("Guess? ") else: print("Congratulations!") Most public spaces have multiple exits, including a fire escape for emergencies. We haven't even touched on try: / except: as a way to escape a while loop. Mostly I just want these considerations to rise to the surface and ruffle the surface awareness of the beginner mind programmer. This is being a good thread for that. Kirby
... another useful contribution to this thread. ---------- Forwarded message ---------- From: Richard Pattis <pattis@ics.uci.edu> Date: Sat, Apr 21, 2012 at 6:53 PM Subject: I'm not allowed to post To: kirby urner <kirby.urner@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. Rich 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 Kirby, 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: ////// break; ////// 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 locality). 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 ]
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 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 9:12 PM To: edu-sig@python.org Subject: Re: [Edu-sig] a short essay about programming ... another useful contribution to this thread. ---------- Forwarded message ---------- From: Richard Pattis <pattis@ics.uci.edu> Date: Sat, Apr 21, 2012 at 6:53 PM Subject: I'm not allowed to post To: kirby urner <kirby.urner@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. Rich 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 Kirby, 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: ////// break; ////// 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 locality). 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@python.org http://mail.python.org/mailman/listinfo/edu-sig
John Zelle wrote: 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") I mostly find John Zelle's version to be more elegant, but I dislike that the line: guess = int(input("Guess? ")) occurs in two places. In general, we should be discouraging writing the same line twice. What if you want to change the prompt? What if you want to create more complex error checking for the input. Are you going to remember to change it in both places? One reasonable option is to break this out into a separate getGuess() function. You'd still end up with two calls to the getGuess() function, but at least the logic of getting the guess could easily be changed in one place. This begs the question, though, as to whether it is possible to rework the code to only have one line which gets the guess, without involving continue, break, or while(True). I don't believe there is a way. If Python had tail recursion, one way to rewrite this that satisfies all those constraints would be: def repeatUntilSecretIsGuessed(): guess = int(input("Guess? ")) if (guess == secret): print("You guessed it") else: print("Nope, try again") repeatUntilSecretIsGuessed() This would be bad form for Python, though. One other thought about while/continue/break. I am always thinking about the fact that we're training kids for the languages and programming styles that will emerge over the next 10+ years. To better understand the future we're preparing them for, I spend a lot of time studying emerging languages, looking for clues about what styles will best "future-proof" my students. In the case of while loops, I think it's instructive to look at Scala, a language that is currently being hailed as the most plausible successor to Java. Scala doesn't have break or continue at all. The idea is that if you have a loop that requires a break, it is far clearer to make that loop into a separate helper function, and use return instead of break. So for example, looking at Kirby's code: while True: # no ifs ands or buts guess = int(input("Guess?: ") if guess == secret: print("You guessed it!") break else: print("Nope, try again...") you'd instead write it as: def repeatUntilSecretIsGuessed(): while True: guess = int(input("Guess?: ") if guess == secret: print("You guessed it!") return # It's a bit easier to understand this code because we see it completely exits the function here, not just the while loop else: print("Nope, try again...") In this example, the distinction seems a little silly, but I would argue that the vast majority of while loops are, semantically speaking, "returning a value". They do this by setting up some accumulator variable before the while loop, and then pounding on the variable, changing it each time through the while loop. It can take a bit of analysis to determine which is the variable(s) you care about, and what it contains at the time you break out of a loop. By breaking the loop into a separate function, and actually returning the value you care about with a return statement, the code becomes much easier to understand. So for example, let's say you want to keep looping until you get a guess from 1 to 10. Standard way (using while True and break) would be something like this: while True: guess = int(input("Guess a number from 1 to 10? ")) if (guess < 1 or guess > 10): print ("Try again") else: break # at this point we continue our code, and we know guess contains a number from 1 to 10 Better way: def getNumberFrom1To10(): while True: guess = int(input("Guess a number from 1 to 10? ")) if (guess < 1 or guess > 10): print ("Try again") else: return guess # Now, it's really obvious what is the value that is being "set" by the while loop. In any case, whether you prefer Kirby's while True version or John's version which requires asking for a guess both before the loop and inside the loop, the main idea here is to get kids thinking each time they have a loop, especially a loop that involves break, whether maybe the code would be better if they factored out the loop into a separate function. --Mark
Several people in this thread have mentioned repeat-until loops, and much of the discussion here seems to be about how to approximate them in Python. In readability dreamland: tries_left = 5 # Must be > 0 repeat: guess = int(input("Guess? ")) tries -= 1 until guess == secret or tries_left == 0 if guess == secret: print("You guessed it!") else: print("You maxed out.") A Racket friend of mine likes to point out the many times in our intro course where we state a problem by saying "do something until X", and then spend time figuring out how to shoehorn the problem into a while loop. (Aside: Mark, I find your while-function approach to be thought provoking.) -Paul Gries On 2012-04-22, at 4:44 PM, Mark Engelberg wrote:
John Zelle wrote: 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")
I mostly find John Zelle's version to be more elegant, but I dislike that the line: guess = int(input("Guess? ")) occurs in two places.
In general, we should be discouraging writing the same line twice. What if you want to change the prompt? What if you want to create more complex error checking for the input. Are you going to remember to change it in both places?
One reasonable option is to break this out into a separate getGuess() function. You'd still end up with two calls to the getGuess() function, but at least the logic of getting the guess could easily be changed in one place.
This begs the question, though, as to whether it is possible to rework the code to only have one line which gets the guess, without involving continue, break, or while(True).
I don't believe there is a way. If Python had tail recursion, one way to rewrite this that satisfies all those constraints would be:
def repeatUntilSecretIsGuessed(): guess = int(input("Guess? ")) if (guess == secret): print("You guessed it") else: print("Nope, try again") repeatUntilSecretIsGuessed()
This would be bad form for Python, though.
One other thought about while/continue/break. I am always thinking about the fact that we're training kids for the languages and programming styles that will emerge over the next 10+ years. To better understand the future we're preparing them for, I spend a lot of time studying emerging languages, looking for clues about what styles will best "future-proof" my students.
In the case of while loops, I think it's instructive to look at Scala, a language that is currently being hailed as the most plausible successor to Java. Scala doesn't have break or continue at all. The idea is that if you have a loop that requires a break, it is far clearer to make that loop into a separate helper function, and use return instead of break. So for example, looking at Kirby's code:
while True: # no ifs ands or buts guess = int(input("Guess?: ") if guess == secret: print("You guessed it!") break else: print("Nope, try again...")
you'd instead write it as:
def repeatUntilSecretIsGuessed(): while True: guess = int(input("Guess?: ") if guess == secret: print("You guessed it!") return # It's a bit easier to understand this code because we see it completely exits the function here, not just the while loop else: print("Nope, try again...")
In this example, the distinction seems a little silly, but I would argue that the vast majority of while loops are, semantically speaking, "returning a value". They do this by setting up some accumulator variable before the while loop, and then pounding on the variable, changing it each time through the while loop. It can take a bit of analysis to determine which is the variable(s) you care about, and what it contains at the time you break out of a loop. By breaking the loop into a separate function, and actually returning the value you care about with a return statement, the code becomes much easier to understand.
So for example, let's say you want to keep looping until you get a guess from 1 to 10.
Standard way (using while True and break) would be something like this:
while True: guess = int(input("Guess a number from 1 to 10? ")) if (guess < 1 or guess > 10): print ("Try again") else: break # at this point we continue our code, and we know guess contains a number from 1 to 10
Better way:
def getNumberFrom1To10(): while True: guess = int(input("Guess a number from 1 to 10? ")) if (guess < 1 or guess > 10): print ("Try again") else: return guess # Now, it's really obvious what is the value that is being "set" by the while loop.
In any case, whether you prefer Kirby's while True version or John's version which requires asking for a guess both before the loop and inside the loop, the main idea here is to get kids thinking each time they have a loop, especially a loop that involves break, whether maybe the code would be better if they factored out the loop into a separate function.
--Mark _______________________________________________ Edu-sig mailing list Edu-sig@python.org http://mail.python.org/mailman/listinfo/edu-sig
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@wartburg.edu> Sent: Saturday, April 21, 2012 11:13pm To: "kirby urner" <kirby.urner@gmail.com>, "edu-sig@python.org" <edu-sig@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 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 9:12 PM To: edu-sig@python.org Subject: Re: [Edu-sig] a short essay about programming ... another useful contribution to this thread. ---------- Forwarded message ---------- From: Richard Pattis <pattis@ics.uci.edu> Date: Sat, Apr 21, 2012 at 6:53 PM Subject: I'm not allowed to post To: kirby urner <kirby.urner@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. Rich 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 Kirby, 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: ////// break; ////// 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 locality). 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@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
Give me loop invariants or give me death! A. Jorge Garcia Applied Math & CS http://shadowfaxrant.blogspot.com http://www.youtube.com/calcpage2009 -- Sent from my Android phone with K-9 Mail. Please excuse my brevity. david@handysoftware.com wrote: 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@wartburg.edu> Sent: Saturday, April 21, 2012 11:13pm To: "kirby urner" <kirby.urner@gmail.com>, "edu-sig@python.org" <edu-sig@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 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 9:12 PM To: edu-sig@python.org Subject: Re: [Edu-sig] a short essay about programming ... another useful contribution to this thread. ---------- Forwarded message ---------- From: Richard Pattis <pattis@ics.uci.edu> Date: Sat, Apr 21, 2012 at 6:53 PM Subject: I'm not allowed to post To: kirby urner <kirby.urner@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. Rich 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 Kirby, 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: ////// break; ////// 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 locality). 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@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
My final project this year in AP Calculus BC was to investigate the use of a Computer Algebra System such as http://www.sagemath.org with Calculus. We introduced SAGE and python by doing some Arithmetic, Algebra and Trig. We even derived Euler's Identity! Let me know what you think: http://shadowfaxrant.blogspot.com/2012/06/teaching-ap-calculus-life-after.ht... HTH, A. Jorge Garcia Applied Math and CompSci http://shadowfaxrant.blogspot.com http://www.youtube.com/calcpage2009
http://shadowfaxrant.blogspot.com/2012/06/teaching-ap-calculus-lac-2012-life... Here's a little update on all the Calculus and SAGE I did with my students these last few weeks as a final project after the AP Calculus BC exam! For most of my students, this is the first time they see SAGE. Since they need to use a Graphing Calculator extensively on the AP exam, I can't really use SAGE during the school year. If I could, I would! Enjoy, A. Jorge Garcia Applied Math and CompSci http://shadowfaxrant.blogspot.com http://www.youtube.com/calcpage2009 -----Original Message----- From: A. Jorge Garcia <calcpage@aol.com> To: edu-sig <edu-sig@python.org> Sent: Sat, Jun 2, 2012 8:36 pm Subject: LAC: Life After Calculus Final Project! My final project this year in AP Calculus BC was to investigate the use of a Computer Algebra System such as http://www.sagemath.org with Calculus. We introduced SAGE and python by doing some Arithmetic, Algebra and Trig. We even derived Euler's Identity! Let me know what you think: http://shadowfaxrant.blogspot.com/2012/06/teaching-ap-calculus-life-after.ht... HTH, A. Jorge Garcia Applied Math and CompSci http://shadowfaxrant.blogspot.com http://www.youtube.com/calcpage2009
I blogged about Khan Academy recently. If you've seen my post, you may want to revisit it as I've been updating it over the last few weeks. I've been getting a lot of email queries about SmartBoarding and ScreenCasting so I've been adding them to this blog post: http://shadowfaxrant.blogspot.com/2012/07/kahn-academy-savior-of-american.ht... HTH, A. Jorge Garcia Applied Math & CompSci http://shadowfaxrant.blogspot.com http://ww.youtube.com/calcpage2009
On Sun, Jul 29, 2012 at 4:10 PM, A. Jorge Garcia <calcpage@aol.com> wrote:
I blogged about Khan Academy recently. If you've seen my post, you may want to revisit it as I've been updating it over the last few weeks. I've been getting a lot of email queries about SmartBoarding and ScreenCasting so I've been adding them to this blog post:
http://shadowfaxrant.blogspot.com/2012/07/kahn-academy-savior-of-american.ht...
HTH, A. Jorge Garcia Applied Math & CompSci http://shadowfaxrant.blogspot.com http://ww.youtube.com/calcpage2009
I've been writing about Khan Academy too sometimes: """ Keith Devlin's idea at the Oregon Math Summit (1997) was that basic competence in computation and numeracy needed to diffuse throughout the curriculum, so that these generic life skills would not be overly confused with "mathematics" in particular. I support that idea, but in part I see the process ("cultural osmosis" you could call it) happening outside the school. 'Sesame Street' marked a turning point in elementary education in many ways. 'Khan Academy' is just code for didactic content in short clips, i.e. 'Sesame Street' at a higher level (might still feature animation and puppets -- Khan's are one aesthetic among many). In other words, if we're talking about how to advance the level of STEM sophistication in the broader culture, among youth and oldsters alike, it's not a given that "aligning the the Common Core" is where to focus -- more a side show than a big tent activity. We have far more leverage in other areas, such as in the content of Saturday morning cartoons. Bill Nye the Science Guy made a big difference to STEM and yet those silly DCers don't award "best teacher" to TV personalities, because they're too busy inspiring a different set of vocations as encoded in Department of Labor catalogs (i.e. "TV personality" is not coded as "Teacher"). Katie Couric is not considered a "teacher". She's an "anchor". Such are the exigencies of the English language and its many calcified / codified grammars (language games). """ http://mathforum.org/kb/message.jspa?messageID=7854797 Kirby
Ever hear of PAEMST? Take a look at this blog post where I research past and present winners of PAEMST: http://shadowfaxrant.blogspot.com/2012/08/paemst-nobel-prize-for-stem-teache... HTH, A. Jorge Garcia Applied Math and CompSci http://shadowfaxrant.blogspot.com http://www.youtube.com/calcpage2009
Hi Again, Of course, David is right here. My previous statement only applies to code where all state change is effected by the code itself. All bets are off in a multi-thread or multi-process situation. That is what makes concurrent programs devilishly hard to reason about. But I wouldn't use that as an excuse to make single-threaded code hard to reason about as well. --John John Zelle, PhD Professor of Computer Science Wartburg College ________________________________________ From: david@handysoftware.com [david@handysoftware.com] Sent: Sunday, April 22, 2012 5:06 PM To: John Zelle Cc: kirby urner; edu-sig@python.org Subject: Re: [Edu-sig] a short essay about programming 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@wartburg.edu> Sent: Saturday, April 21, 2012 11:13pm To: "kirby urner" <kirby.urner@gmail.com>, "edu-sig@python.org" <edu-sig@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 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 9:12 PM To: edu-sig@python.org Subject: Re: [Edu-sig] a short essay about programming ... another useful contribution to this thread. ---------- Forwarded message ---------- From: Richard Pattis <pattis@ics.uci.edu> Date: Sat, Apr 21, 2012 at 6:53 PM Subject: I'm not allowed to post To: kirby urner <kirby.urner@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. Rich 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 Kirby, 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: ////// break; ////// 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 locality). 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@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
participants (8)
-
A. Jorge Garcia
-
david@handysoftware.com
-
dsnou849@gmail.com
-
John Zelle
-
kirby urner
-
Kirby Urner
-
Mark Engelberg
-
Paul Gries