Previous Page
Next Page

9.6. Default Argument Values

Resolve any default argument values as soon as @_ is unpacked.

The fundamental rule of argument processing is: nothing happens in the subroutine until all the arguments are stable. Don't, for example, add in defaults on the fly:

    Readonly my $DEF_PAGE_WIDTH => 78;
    Readonly my $SPACE          => q{ };

    sub padded {
        my ($text, $arg_ref) = @_;

        # Compute left and right spacings...
        my $gap   = ($arg_ref->{cols}||$DEF_PAGE_WIDTH) - length($text||=$EMPTY_STR);
        my $left  = $arg_ref->{centered} ? int($gap/2) : 0;
        my $right = $gap - $left;

        # Prepend and append space...
        my $filler = $arg_ref->{filler} || $SPACE;
        return $filler x $left . $text . $filler x $right;
    }

Apart from making the gap computation much harder to read and to verify, using the || and ||= operators to select default values is equivalent to testing for truth, and therefore much more prone to error on the edge cases (such as a '0' fill character).

If default values are needed, set them up first. Separating out any initialization will make your code more readable, and simplifying the computational statements is likely to make them less buggy too:


    sub padded {
        my ($text, $arg_ref) = @_;

        
# Set defaults...
        #            If option given...          Use option           Else default
my $cols = exists $arg_ref->{cols} ? $arg_ref->{cols} : $DEF_PAGE_WIDTH; my $filler = exists $arg_ref->{filler} ? $arg_ref->{filler} : $SPACE;

        # Compute left and right spacings...
my $gap = $cols - length $text; my $left = $arg_ref->{centered} ? int($gap/2) : 0; my $right = $gap - $left;
# Prepend and append space...
return $filler x $left . $text . $filler x $right; }

If there are many defaults to set up, the cleanest way to do that is by factoring the defaults out into a table (i.e., a hash) and then pre-initializing the argument hash with that table, like so:


    Readonly my %PAD_DEFAULTS => (
        cols     => 78,
        centered => 0,
        filler   => $SPACE,
        
# etc.
); sub padded { my ($text, $arg_ref) = @_;
# Unpack optional arguments and set defaults...
my %arg = ref $arg_ref eq 'HASH' ? (%PAD_DEFAULTS, %{$arg_ref}) : %PAD_DEFAULTS;
# Compute left and right spacings...
my $gap = $arg{cols} - length $text; my $left = $arg{centered} ? int($gap/2) : 0; my $right = $gap - $left;
# Prepend and append space...
return $arg{filler} x $left . $text . $arg{filler} x $right; }

When the %arg hash is initialized, the defaults are placed ahead of the arguments supplied by the caller ((%PAD_DEFAULTS, %{$arg_ref})). So the entries in the default table are assigned to %arg first. Those default values are then overwritten by any entries from $arg_ref.

    Previous Page
    Next Page