Item 24: Avoid using _ directlyunless you have to.


Item 24: Avoid using @_ directlyunless you have to.

Unlike many programming languages, Perl has no built-in support for named or "formal" parameters. The arguments to a subroutine are always passed in via the argument list variable @_ . It is up to the author of the subroutine to give the arguments names and check them for consistency. In general, you should always start off a subroutine by copying its arguments and giving them names . The preferred method is to use my :

This subroutine will remove digits from a string.

 sub digits_gone {    my $str = shift;    $str =~ tr/0-9//d;    $str;  } 

Default arg for shift in sub is @_ (see Item 8 ) .

Return translated string.

The idiomatic way to read arguments passed to a subroutine is to use shift to get them one at a time or a list assignment to read them all:

This subroutine counts different types of chars in a string.

Usage: char_count $str, $chars1, $chars2, ...

 sub char_count {    my $str = shift;    my @chars = @_;    my @counts;    for (@chars) {      $_ =~ s/\/\\/g;      $_ =~ s#/#\/#g;      push @counts,          eval "$str =~ tr/$_//";    }    @counts;  } 

Get first arg.

Get rest into @chars .

Handle special chars.

Count chars with tr/// (see Item 20 ).

Return list of counts.

Another reason to copy and name subroutine arguments is that the elements of @_ are actually aliases for the values that were passed in. Modifying an element of @_ modifies the corresponding subroutine argumenta sort of " call by reference" semantics. Subroutines can modify their arguments, but attempts to do so can fail if the arguments are read-only:

 sub txt_file_size {    $_[0] .= '.txt' unless /\.\w+$/;    -s $_[0];  } 

POOR style. We try to automatically append '.txt' to extension-less files, but this is goofy.

If we try to call this subroutine as txt_file_size "test" , it fails with an error message as it tries to modify the read-only value "test" . Also, if we call it with a modifiable argument, say txt_file_size $myfile , it may append '.txt' to the argument, which is likely not what we want.

But sometimes this aliasing "feature" turns out to be genuinely useful. For example:

This subroutine will normalize some values "in place."

 sub normalize_in_place {    my $max = 0;    for (@_) {      $max = abs($_) if abs($_) > $max;    }    return unless $max;    for (@_) { $_ /= $max }    return;  } 

Find maximum magnitude of args.

Normalize args. Note that $_ is an "alias of an alias"works fine!

 ($x, $y, $z) = 1..3;  normalize_in_place $x, $y, $z;  printf "%.2g %.2g %.2g\n",    $x, $y, $z; 

0.33 0.67 1

If you are trying to optimize for speed, it may be faster for you to use @_ without copying it, because copying values takes a significant amount of time. If you do so, remember that array subscripting tends to be slow, so try to use constructs like foreach , grep , and map that allow you to iterate over an array without having to subscript its elements repeatedly. The best approach, of course, would be to write two or more different versions and Benchmark them (see Item 37).

Even though subroutine arguments are passed as aliases, any array arguments is flattened into a list. You can modify the elements of an array argument, but not the array itself:

Throw out the "bad" elementNOT!

 sub no_bad {    for $i (0..$#_) {      if ($_[$i] =~ /^bad$/) {        splice @_, $i, 1;        print "in no_bad: @_\n";        return;      }    }    return;  } 

You can rearrange the contents of @_ all you want, but it won't affect the original arguments.

 @a = qw(ok better fine great bad good);  no_bad @a;  print "after no_bad: @a\n"; 
 

When we run this program, we get:

 %  tryme  in no_bad: ok better fine great good  after no_bad: ok better fine great bad good 

Finally, on a slightly different topic, subroutines that are called with no arguments usually have an empty @_ of their own. However, if a subroutine is called with an ampersand and no parentheses, it inherits the current @_ :

 sub inner {    print "\@_ = @_\n";  } 
 
 sub outer {    &inner;  } 

&inner with no parens is the only syntax that "works."

 outer 1..3; 

@_ = 1 2 3



Effective Perl Programming. Writing Better Programs with Perl
Effective Perl Programming: Writing Better Programs with Perl
ISBN: 0201419750
EAN: 2147483647
Year: 1996
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net