Приглашаем посетить
Майков (maykov.lit-info.ru)

Section 6.5.  C-Style Loops

Previous
Table of Contents
Next

6.5. C-Style Loops

Avoid C-style for statements.

The three-part for statements that Perl inherits from C are needed only for unusual loop control behaviour, such as iterating by twos, or in an irregular sequence. But even in such cases, these C-style loops provide that unusual behaviour in an obscure and harder-to-maintain way.

That's because the iterative behaviour of a three-part for statement is emergent, rather than explicit. In other words, the only way to know what a loop like:

    for (my $n=4; $n<=$MAX; $n+=2) {
        print $result[$n];
    }

is going to do is to sit down and work out the abstract logic of the three components:

"Let's see: n starts at 4, and continues up to MAX, incrementing by two each time. So the sequence is 4, 6, 8, etc. So the loop iterates through all the even n's from 4 up to and including MAX (if MAX itself is even)."

But you could write the same loop without the C-style for, like this:

    RESULT:
    for my $n (4..$MAX) {
        next RESULT if odd($n);
        print $result[$n];
    }

The advantage with this version is that subsequent readers of the code no longer have to work out the logic of the loop. The code itself says explicitly:

"n from 4 to MAX, skipping values that are odd."

The code is clearer, which means it's more maintainable and less susceptible to subtle bugs or nasty edge-cases.

The usual counter-argument is that this second example has to iterate twice as many times for the same effect, and has to call a subroutine (odd( )) each of those times. Should $MAX become large, that additional cost might become prohibitive.

In practice, many loops don't iterate enough times for those overheads to matter. And often the actual work done by the loop will swamp the costs of iteration anyway. But, if benchmarking indicates that the clearer-but-slower code is having a noticeable effect on performance, then a better solution is usually to "inline" the call to even( ), replacing it with a direct check on each $n value:


    RESULT:
    for my $n (4..$MAX) {
        next RESULT if $n % 2;    
# $n%2!=0 when $n is odd
print $result[$n]; }

In a simple example like this one, it can be hard to see the benefits of the more readable non-C-style for loops. But those benefits become clearer as the complexity of the loop control increases. Take a few moments to work out what the following three-part for loop does:

    SALE:
    for (my ($sales, $seats)=(0,$CAPACITY);
         $sales < $TARGET && $seats > $RESERVE;
         $sales += sell_ticket(  ), $seats--
    ) {
        prompt -yn, "[$seats seats remaining] Sell another? "
            or last SALE;
    }

The fact that working out what that loop does really does take a few moments is a good indicator that the code is not sufficiently readableespecially when compared with the following non-C-style version:


    my $sales = 0;
    my $seats = $CAPACITY;

    SALE:
    while ($sales < $TARGET && $seats > $RESERVE) {
        prompt -yn, "[$seats seats remaining] Sell another? "
            or last SALE;

        $sales += sell_ticket(  );
        $seats--;
    }

The three-part for loop is rarely used as a pure for loop (i.e., to iterate a fixed number of times). More often, it's used as a complex while loop, which means that using an actual while loop in such cases is a much better practice.

    Previous
    Table of Contents
    Next