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 _______________________________________________ Edu-sig mailing list Edu-sig@python.org http://mail.python.org/mailman/listinfo/edu-sig