on writing a while loop for rolling two dice

Chris Angelico rosuav at gmail.com
Thu Sep 2 14:58:35 EDT 2021


On Fri, Sep 3, 2021 at 4:33 AM Hope Rouselle <hrouselle at jevedi.com> wrote:
> Yeah.  Here's a little context.  I came across this by processing a list
> of exercises.  (I'm teaching a course --- you know that by now, I
> guess.)  So the first thing I observed was the equal volume of work
> dedicated to while loops and for loops --- so I decided to compared
> which appeared more often in a certain sample of well-written Python
> code.  It turns out the for loop was much more frequent.  Students have
> been reporting too much work in too little time, so I decided to reduce
> the number of exercises involving while loops.  When I began to look at
> the exercises, to see which ones I'd exclude, I decided to exclude them
> all --- lol! --- except for one.  The one that remained was this one
> about rolling dice until a satisfying result would appear.  (All other
> ones were totally more naturally written with a for loop.)
>
> So if I were to also write this with a for-loop, it'd defeat the purpose
> of the course's moment.  Besides, I don't think a for-loop would improve
> the readability here.

It's on the cusp. When you ask someone to express the concept of "do
this until this happens", obviously that's a while loop; but as soon
as you introduce the iteration counter, it becomes less obvious, since
"iterate over counting numbers until this happens" is a quite viable
way to express this. However, if the students don't know
itertools.count(), they'll most likely put in an arbitrary limit (like
"for c in range(100000000)"), which you can call them out for.

> But I thought your protest against the while-True was very well put:
> while-True is not too readable for a novice.  Surely what's readable or
> more-natural /to someone/ is, well, subjective (yes, by definition).
> But perhaps we may agree that while rolling dice until a certain
> success, we want to roll them while something happens or doesn't happen.
> One of the two.  So while-True is a bit of a jump.  Therefore, in this
> case, the easier and more natural option is to say while-x-not-equal-y.

That may be the case, but in Python, I almost never write "while
True". Consider the two while loops in this function:

https://github.com/Rosuav/shed/blob/master/autohost_manager.py#L92

Thanks to Python's flexibility and efficient compilation, these loops
are as descriptive as those with actual conditions, while still
behaving exactly like "while True". (The inner loop, "more pages",
looks superficially like it should be a for loop - "for page in
pages:" - but the data is coming from successive API calls, so it
can't know.)

> I don't see it.  You seem to have found what we seem to agree that it
> would be the more natural way to write the strategy.  But I can't see
> it.  It certainly isn't
>
> --8<---------------cut here---------------start------------->8---
> def how_many_times_1():
>   c, x, y = 0, None, None
>   while x != y:
>     c = c + 1
>     x, y = roll()
>   return c, x, y
> --8<---------------cut here---------------end--------------->8---
>
> nor
>
> --8<---------------cut here---------------start------------->8---
> def how_many_times_2():
>   c, x, y = 0, None, None
>   while x == y:
>     c = c + 1
>     x, y = dados()
>   return c, x, y
> --8<---------------cut here---------------end--------------->8---
>
> What do you have in mind?  I couldn't see it.

You're overlaying two loops here. One is iterating "c" up from zero,
the other is calling a function and testing its results. It's up to
you which of these should be considered the more important, and which
is a bit of extra work added onto it. With the counter as primary, you
get something like this:

for c in itertools.count():
    x, y = roll()
    if x == y: return c, x, y

With the roll comparison as primary, you get this:

c, x, y = 0, 0, 1
while x != y:
    x, y = roll()
    c += 1
return c, x, y

Reworking the second into a do-while style (Python doesn't have that,
so we have to write it manually):

c = 0
while "x and y differ":
    x, y = roll()
    c += 1
    if x == y: break
return c, x, y

And at this point, it's looking pretty much identical to the for loop
version. Ultimately, they're all the same and you can pick and choose
elements from each of them.

ChrisA


More information about the Python-list mailing list