Документация
HTML CSS PHP PERL другое

Section 6.20.  Distributed Control

 
Previous
Table of Contents
Next

6.20. Distributed Control

Don't contort loop structures just to consolidate control.

The bloated conditional tests mentioned in the previous guideline can also appear in the conditions of loop structures, where they usually indicate the (mis)application of structured programming techniques.

Proponents of structured programming usually insist that every loop should have only a single exit point: the conditional expression that's controlling the loop. The very laudable intent of that rule is to make it easier to determine the correctness of the loop by consolidating all information about its termination behaviour in a single place.

Unfortunately, blind adherence to this principle frequently produces code that looks like this:

    Readonly my $INTEGER => qr/\A [+-]? \d+ \n? \z/xms;

    my $int   = 0;
    my $tries = 0;
    my $eof   = 0;

    while (!$eof
           && $tries < $MAX_TRIES
           && ( $int !~ $INTEGER || $int < $MIN_BIG_INT )
    ) {
        print 'Enter a big integer: ';
        $int = <>;
        if (defined $int) {
            chomp $int;

            if ($int eq $EMPTY_STR) {
                $int = 0;
                $tries--;
            }
        }
        else {
            $eof = 1;
        }
        $tries++;
    }

The loop conditional typically contains a mixture of positive and negative tests on several flag variables. The block itself then contains multiple nested if tests, mainly to set the termination flags or to pre-empt further execution if an exit condition is encountered within the block.

When a loop has been contorted in this manner, it's often extremely difficult to understand. Take a moment to work through the previous example code and determine exactly what it does.

Now compare that convoluted code with the following version (which provides exactly the same behaviour):


    Readonly my $INTEGER => qr/\A [+-]? \d+ \n? \z/xms;

    my $int;

    INPUT:
    for my $attempt (1..$MAX_TRIES) {
        print 'Enter a big integer: ';
        $int = <>;

        last INPUT if not defined $int;
        redo INPUT if $int eq "\n";
        next INPUT if $int !~ $INTEGER;

        chomp $int;
        last INPUT if $int >= $MIN_BIG_INT;
    }

This version requires no flag variables. It has fewer lines of code. It has no nested conditionals or multipart tests. You can easily work out what it does on end-of-file or when the input is an empty line or when given a non-integer simply by working through the linear sequence of tests within the block.

Herding loop flags into a single location only gives the illusion of consolidating control. A complex exit condition still relies on other tests within the loop to set the appropriate flags, so the actual control is still implicitly distributed.

Perl provides clean ways to distribute control explicitly throughout a loop. Use them.

    Previous
    Table of Contents
    Next
    © 2000- NIV