Приглашаем посетить
Батюшков (batyushkov.lit-info.ru)

Section 6.6.  Unnecessary Subscripting

Previous
Table of Contents
Next

6.6. Unnecessary Subscripting

Avoid subscripting arrays or hashes within loops.

Unless you actually need to know the indices of the array elements you're processing, iterate the values of an array directly:


    for my $client (@clients) {
        $client->tally_hours(  );
        $client->bill_hours(  );
        $client->reset_hours(  );
    }

Iterating the indices and then doing repeated array accesses is significantly slower, and less readable:

    for my $n (0..$#clients) {
        $clients[$n]->tally_hours(  );
        $clients[$n]->bill_hours(  );
        $clients[$n]->reset_hours(  );
    }

Repeated indexing is repeated computation; duplicated effort that incurs an extra cost but provides no added benefit. Iterating indices is also prone to off-by-one errors. For example:

    for my $n (1..@clients) {
        $clients[$n]->tally_hours(  );
        $clients[$n]->bill_hours(  );
        $clients[$n]->reset_hours(  );
    }

Likewise, if you're processing the entries of a hash and you need only the values of those entries, don't iterate the keys and then look up the values repeatedly:

    for my $original_word (keys %translation_for) {
       if ( $translation_for{$original_word} =~ m/ $PROFANITY /xms) {
           $translation_for{$original_word} = '[DELETED]';
       }
    }

Repeated hash look-ups are even more costly than repeated array indexing. Just iterate the hash values directly:


    for my $translated_word (values %translation_for) {
       if ( $translated_word =~ m/ $PROFANITY /xms) {
           $translated_word = '[DELETED]';
       }
    }

Note that this last example works correctly because, in Perl 5.6 and later, the values function returns a list of aliases to the actual values of the hash, rather than just a list of copies (see "Hash Values" in Chapter 8). So if you change the iterator variable (for example, assigning '[DELETED]' to $translated_word), you're actually changing the corresponding original value inside the hash.

The only situation where iterating values doesn't work correctly is when you need to delete the entries from the hash:

    for my $translated_word (values %translation_for) {
       if ( $translated_word =~ m/ $PROFANITY /xms) {
           delete $translated_word;                    # Compile-time error
       }
    }

Here, aliasing isn't enough, because the delete builtin needs to know the key as well, so it will only accept actual hash look-ups as arguments. The correct solution is to use a hash slice instead (see Chapter 5):


    my @unacceptable_words
        = grep {$translation_for{$_} =~ m/ $PROFANITY /xms}
               keys %translation_for;

    delete @translation_for{@unacceptable_words};

The grep collects all those keys whose values must be removed, and stores that list in @unacceptable_words. The list of keys is then used to create a slice of the original hash (i.e., a list of hash look-ups), which can be passed to delete.

By Any Other Name . . .

An alias may sometimes seem like magic, but it's based in a very simple idea.

In a Perl program, a normal variable consists of two distinct components: a storage location in memory, and a name (such as $foo) by which the program refers to that storage location. In other words, every variable is a box, with a "Hi-my-name-is..." sticker on it.

Aliasing is the process of putting a second (or third, or nth) "Hi-my-name-is-also..." sticker on a single box. Perl subroutines do this all the time. For example, if you call get_passwd($user), then inside the call to get_passwd( ) the name $_[0] is temporarily attached to the container whose original name is $user. That container now has two names: one that's used inside the subroutine and one that's used outside.

Anything you do to an alias (e.g., get its value, increment it, print it, assign a new value to it) is really being done to the original variablebecause there's really only one variable, no matter how many separate names you give it.


    Previous
    Table of Contents
    Next