6.7. Necessary Subscripting
Sometimes you have no choice: you really do need to know the index of each value you're iterating over, as well as the value itself. But, even when it is necessary to iterate indices or keys, be sure to extract the value only once:
for my $agent_num (0..$#operatives) { Never extract it repeatedly in the same iteration: for my $agent_num (0..$#operatives) { # Iterate indices print "Checking agent $agent_num\n"; # Use index if ($on_disavowed_list{$operatives[$agent_num]}) { # Extract value print "\t...$operatives[$agent_num] disavowed!\n"; # Extract value again } } Apart from the fact that repeated array look-ups are repeatedly expensive, they also clutter the code, and increase the maintenance effort if either the array name or the name of the iterator variable subsequently has to be changed. Occasionally a mere copy of the value won't do, because you need to iterate both indices and values, and still be able to modify the values. It's easy to do that toojust use the Data::Alias CPAN module[*]:
use Data::Alias;
for my $agent_num (0..$#operatives) { The technique here is the same as before: iterate indices, then look up the value once. But in this case, instead of copying the value, the loop block creates a lexically scoped alias to it. The alias function takes a variable as its only argument, and converts that variable into an alias variable. Assigning directly to the alias variable as you declare it:
alias my $agent causes the alias variable to become another name for the assigned expression (usually another variable). Once the aliasing is accomplished, anything that is done to the alias variable (including subsequent assignments) is actually being done to the variable to which it was aliased. Thus assigning to $agent actually assigns to $operatives[$agent_num]. The only difference is that there's no array element look-up involved, so accessing the alias is actually faster. Exactly the same approach can be taken when it's necessary to iterate the keys and values of a hash:
for my $name (keys %client_named) { Note that you can't use Perl's built-in each function for this kind of thing: while (my ($name, $client_info) = each %client_named) { # Iterate key/value print "Checking client $name\n"; # Use key if ($client_info->inactivity( ) > $ONE_YEAR) { # Use value $client_info # Change copy (!) = Client::Moribund->new({ from => $client_info }); # ...using value } } If you use each, the $client_info variable receives only a copy of each hash value, not an alias to it. So changing $client_info has no effect on the original values inside %client_named. Extracting or renaming values is also a good practice if those values are nested inside a deeper data structure, or are the result of a subroutine call. For example, if the previous code needed to choose different responses for clients with different periods of inactivity, it would be cleaner and more efficient to extract that information only once:
for my $name (keys %client_named) { |