# a short essay about programming

11 messages
Open this post in threaded view
|
Report Content as Inappropriate

## a short essay about programming

Open this post in threaded view
|
Report Content as Inappropriate

## Re: a short essay about programming

Open this post in threaded view
|
Report Content as Inappropriate

## Re: a short essay about programming

Open this post in threaded view
|
Report Content as Inappropriate

## Re: a short essay about programming

 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 <[hidden email]> 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, PhDProfessor of Computer ScienceWartburg CollegeFrom: edu-sig-bounces+john.zelle=wartburg.edu@python.org [edu-sig-bounces+john.zelle=wartburg.edu@python.org] on behalf of John Zelle [[hidden email]]Sent: Saturday, April 21, 2012 8:11 AMTo: kirby urner; [hidden email]Subject: Re: [Edu-sig] a short essay about programmingKirby,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 yo u 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 falseprint("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 = 5guess = int(input("Guess? "))tries = 1while guess != secret and tries < allowed: //user gets to try again print("Nope, try again") guess = int(input("Guess? ")) tries += 1if 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, PhDProfessor of Computer ScienceWartburg CollegeFrom: edu-sig-bounces+john.zelle=wartburg.edu@python.org [edu-sig-bounces+john.zelle=wartburg.edu@python.org] on behalf of kirby urner [[hidden email]]Sent: Saturday, April 21, 2012 2:45 AMTo: [hidden email]Subject: [Edu-sig] a short essay about programmingA common "error" (not too serious) that I see inbeginning Python (and no doubt other languages,but Python is the one I'm teaching), is having awhile condition that appears to put a lid on things,but then the flow all leaks away through breakstatements, such that the "front door" conditionis 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 younever really go back to the top in the casewhere you'd get to leave. Rather, you exit bythe back door, through break.So in that case, wouldn't have been simpler andmore elegan t, 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 bethe end of the matter, but then a next questionarises: wouldn't this also be a more correctsolution?: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 actuallyexit by means of it, thanks to continue or...while guess != secret: guess = int(input("Guess?: ") if guess == secret: print("You guess ed it!") else: print("Nope, try again...")... thanks to no continue. This last one is gettinga thumbs up, but then I'd pause here and say"continue" can be easier on the eyes. It'sunambiguous where it takes you, in contrastto having to scan on down the gauntlet, lookingfor possibly other open doors. "What happensnext" should not require scanning ahead toofar. Help us not to get lost. Be a civic-mindedcoder.I'm thinking of a programming style that advisestwo things:(a) if you use a while condition that's variable,that's expected to change, then your goal shouldbe to always exit because of that, i.e. that shouldbe your only exit point. Even if some othercriterion suggests exiting, you have the optionto flip that "lid" at the top, to crack that frontdoor, and bounce the ball out.(b) which is why 'continue' is your frie nd. Youare showing the user where your 'break' statementsare, except you don't use "break" statements, asyou've given a non-constant condition, and youraim is to make that your ONLY exit point.In short: never use break to exit a while loopunless your condition is while True.Instead, always flip the condition and exitthrough the font door.However, as soon as I make that rule I canthink of good reasons to break it. The otherprogrammers in the room are shaking theirheads. Won't we lose information, neededelsewhere in the program, if we "artificially"force a True condition to False. Aren't we, ineffect, lying? That concern could be addressed.Keep all the info true, just treat the "lidcondition" (it "keeps a lid on it") as a flag.Go ahead and remember the user's guess.allowed = 5tries = 1exit = Falsewhile 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 continueI think we all understand the main issue:writing reader-friendly code, and rules for doingso. There's something comforting about approachinga 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 wanta minimum of two exits usually: when anotherround is called for versus when we've "hit bottom".Can we have it both ways?Conclusions:Lets not be too hasty with rules of thumband:Lets keep the reader in mind when writing code. Just because the interpreter knows to computethe flow unambiguously, doesn't mean all waysof writing it are equally reader-friendly.What may seem a gratuitous gesture, anunnecessary flourish, may actually promotereader comprehension of your code, and thatshould be a goal as much as satisfying theinterpreter.KirbyEdu-sig mailing list[hidden email]http://mail.python.org/mailman/listinfo/edu-sigEdu-sig mailing list[hidden email]http://mail.python.org/mailman/listinfo/edu-sigEdu-sig mailing list[hidden email]http://mail.python.org/mailman/listinfo/edu-sig```_______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig
Open this post in threaded view
|
Report Content as Inappropriate

## Re: a short essay about programming

 In reply to this post by John Zelle On Sat, Apr 21, 2012 at 6:11 AM, John Zelle <[hidden email]> 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 _______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig
Open this post in threaded view
|
Report Content as Inappropriate

## Re: a short essay about programming

Open this post in threaded view
|
Report Content as Inappropriate

## Re: a short essay about programming

Open this post in threaded view
|
Report Content as Inappropriate

## Re: a short essay about programming

 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 10Better 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 [hidden email] http://mail.python.org/mailman/listinfo/edu-sig
Open this post in threaded view
|
Report Content as Inappropriate

## Re: a short essay about programming

Open this post in threaded view
|
Report Content as Inappropriate

## Re: a short essay about programming

 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.[hidden email] 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" <[hidden email]>Sent: Saturday, April 21, 2012 11:13pmTo: "kirby urner" <[hidden email]>, "[hidden email]" <[hidden email]>Subject: Re: [Edu-sig] a short essay about programmingHi 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.--JohnJohn Zelle, PhDProfessor of Computer ScienceWartburg CollegeFrom: edu-sig-bounces+john.zelle=wartburg.edu@python.org [edu-sig-bounces+john.zelle=wartburg.edu@python.org] on behalf of kirby urner [[hidden email]]Sent: Saturday, April 21, 2012 9:12 PMTo: [hidden email]Subject: Re: [Edu-sig] a short essay about programming... another useful contribution to this thread.---------- Forwarded message ----------From: Richard Pattis <[hidden email]>Date: Sat, Apr 21, 2012 at 6:53 PMSubject: I'm not allowed to postTo: kirby urner <[hidden email]>Feel free to post this for me, which got returned. Probablybecause I use a mail alias and never tried to post before.RichI 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 mightbe on the listserv (more than you want) over the summer as I learnPython and how to program in it and teach it: a tall order for a fewmonths. As Charles McCabe (SF Examiner) said, "Any clod canhave the facts; having opinions is an art." - repKirby,I'm a long time Java teacher (heck, I started teaching Fortran in1976) who will soon be teaching Python. We (here at UC Irvine)are switching to a 3 quarter introduction to programming sequenceall 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 appreciateyour efforts (and have learned from them).I'm a big fan in Java of teaching beginners onlyfor(;;) -I call it for-ever- and if/break termination (although somecolleagues accuse me of teaching machine language in Java).I like forever if/break for a few reasons, among them are Iteach ifs right before looping, so students get a chance toimmediately use ifs; I think it is easier to state thecondition in which to break instead of the condition in whichto continue (although we discuss how to compute one fromthe other by DeMorgan's laws later); there is only one loopingform, so students aren't faced with the up-front decision of whatkind of loop to use; and finally, they can fully write the loop andits body and then worry about termination later (unlike a whileloop, where syntactically the first thing you must write is thecontin uation test -although some computer scientists wouldconsider 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) usingforever if/break, although later I illustrate the semantics of Java'sother looping forms, using forever if/break (which they are familiarwith), tell them if the pattern fits, do the transformation to simplifytheir code, and if you do this enough, later in the course you willwrite the "right" pattern first, without thinking about the transformation.I'm not a big fan of continue here. I conceptualize this as anN=1/2 loop (sentinel loops are also of this form) and am happywith forever if/break; The question I'd have for a while loop, iswhere does guess come from the first time the test is evaluated;if the answer is some initialization that is not equal to secret, I'dsay yuck: guess sh ould store the user's guesses only, not some crazyvalue 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 timearound, it doesn't jump out at me (same, but a little less so, for yourexit = True code in the later example; more soon). With break, you recognizetermination and perform the action immediately (like return). Also,you have to have the condition stated twice in your final code, in twoopposite forms (guess != secret) and also (guess == secret); confusingand 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 alwaysfor beginners. I also require (for most of the quarter) at most onebreak 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 itIn the next code you set exit = True then continue so the nextiteration terminatesthe loop, which I think is also bad because of non-locality. You areterminating indirectly all over the loop. I tell my students sinceloop terminationis what the loop is all about, it should be obvious where you areterminating and why. I start by making them put their break statements incomment sandwiches, so they stand out: ////// break; //////OK, so how would I write your loop to match its semantics, but beas simple as possible? Before showing my code, I dislike the factthat 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 initializedat 1, is incremented before it is tested == allowed); in other casesit is impoliteand gives the user one fewer try than they should get; so I'll fixthose bugs (youcould fix them by setting tries to 0, which I think is right, becausethe user hasn'ttried yet; I'd increment tries right before or after the user enters anew guess: morelocality).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 getsat least one guess.allowed = 5tries = 0while 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 toreduce the size (but possibly increase the complexity of understandingthis 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 bothmight be true,but the secret test does what you want.What I like about this solution is it localizes the termination test (onebreak) and the final disposition of the game (one "you won/lost"statement). What I dislike about it (I'm no Pygmalion) is the uncouplingbetween the termination and printing, and it has "redundant " tests(now three, instead of two: one of my big complaints in the code youstarted 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 tominimize 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[hidden email]http://mail.python.org/mailman/listinfo/edu-sigEdu-sig mailing list[hidden email]http://mail.python.org/mailman/listinfo/edu-sigEdu-sig mailing list[hidden email]http://mail.python.org/mailman/listinfo/edu-sig```_______________________________________________ Edu-sig mailing list [hidden email] http://mail.python.org/mailman/listinfo/edu-sig