Looping [was Re: Python and the need for speed]

Steve D'Aprano steve+python at pearwood.info
Wed Oct 11 21:26:07 EDT 2017


On Wed, 11 Oct 2017 10:57 pm, Stefan Ram wrote:

>  FWIW, in is book "Touch of Class" (2009) Bertrand Meyer writes:
> 
> |Such instructions are just the old goto in sheep's clothing.
> |Treat them the same way as the original:
> |
> |/Touch of Methodology/:
> | Sticking to one-entry, one-exit building blocks
> |Stay away from any "break" or similar control mechanism.

I have a great deal of respect for Meyer, but in this case I think he has it
badly wrong on both counts (`break` and "one-entry, one-exit").

Unrestricted GOTO (as in BASIC, circa 1970, where you could jump to any line
in the code, or as in assembly) is rightly considered harmful (albeit
necessary in assembly). But if Meyer means *any* sort of jump ("similar
control mechanism"), then that would rule out not just `break` and `continue`
but also:

    * while loops
    * for loops
    * if...else
    * case/switch statements
    * calling subroutines (functions or procedures)
    * exception handling

Remember that a function call is basically a GOTO in disguise: execution jumps
to the function, and jumps back when the function returns. In BASIC, there
was a GOSUB intermediate between a GOTO and a procedure call.

Surely Meyer doesn't mean to say we should never call functions or use
`while`, `for` or `if`. So he has to distinguish between kinds of jumps:

Good jumps (I presume):

    * function calls
    * if...else
    * looping

Evil jumps:

    * unrestricted BASIC-style GOTO/GOSUB any line number
    * break/continue

Not sure:

    * GOTO where there are restrictions on where you can jump
    * COMEFROM

(I kid: I'm sure Meyer would oppose COMEFROM, and I expect that even
Pascal-style restricted GOTO would be on his "evil" list to avoid.)

So the question becomes, why are such harmless, simple to understand,
innocuous jumps like `break` and `continue` in the evil list, when they not
only simplify code but make it more efficient?

# with break
for i in range(2**64):
    if isprime(i):
        print(i, "is prime")
        break

# without break
still_searching = True
for i in range(2**64):
    if still_searching and isprime(i):
        print(i, "is prime")
        still_searching = False

# without break, attempt number 2
still_searching = True
i = 0
while still_searching and i < 2**64:
    if isprime(i):
        print(i, "is prime")
        still_searching = False


Unrestricted jumps *into* a subroutine are hard to reason about. In general,
subroutines should have a single entry point. But the requirement for one
exit is too strict. From the caller's perspective, there is no way to tell
how many exit point a function has: subroutines are black boxes that the
caller cannot see into, and the difference between a single exit and multiple
exits is invisible.

But from the point of view of the subroutines, the rule "one exit" is like the
rule "no break" for loops: it makes the code more complex and less efficient.
If you're done, you're done, and you might as well return out of the function
rather than write boilerplate code to pad it out until you get to the very
end. With only a single condition to test, there's not much difference
between the two:

# with single exit                      # with multiple exits
def foo():                              def foo():
    if condition:                           if condition:
        result = 1                              return 1
    else:                                   return 2
        result = 2
    return result


but as the number of decision points increase, the complexity required to keep
a single exit also increases.
  
Ironically, after telling us to stick to code with one entry and one exit,
Meyer then contradicts himself by recommending exceptions:

> |You can use exception handling as a technique of last resort
> |to handle unexpected events for which the normal control
> |structures let you down.

Even more ironically, exception handling is most similar to a COMEFROM, which
was invented as a joke.



-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list