[Edu-sig] a short essay about programming

Paul Gries pgries at cs.toronto.edu
Mon Apr 23 10:49:35 CEST 2012

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

    guess = int(input("Guess? "))
    tries -= 1
until guess == secret or tries_left == 0

if guess == secret:
    print("You guessed it!")
    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 at python.org
> http://mail.python.org/mailman/listinfo/edu-sig

More information about the Edu-sig mailing list