Home > Software design >  Uninitialized value when moving sorting function to package
Uninitialized value when moving sorting function to package

Time:10-07

Say I got a file main.pl and a package package.pm

In main.pl I got the function

sub GetTimestampFromDateStr {
        my $date_str = shift;

        $logger->debug("[GetTimestampFromDateStr]:(Checking date \"$date_str\"\n"); # Here

        my $unix_timestamp;
        if ( $date_str =~ /^\d{8}\.\d{6}$/ ) { # Here
            $unix_timestamp =
            Time::Piece->strptime( $date_str, '%Y%m%d.%H%M%S' )->epoch();
        }
        else {
            $logger->error( # Here
              "[GetTimestampFromDateStr]:(Invalid date format for \"$date_str\", must be \"%Y%m%d.%H%M%S\"  i.e. \"20170125.123500\"\n"
            );
        }

        return $unix_timestamp;
    }

which gets called by

sub DATESORT {
        my $TYPE = shift;

        my $ts_a = GetTimestampFromDateStr($a);
        my $ts_b = GetTimestampFromDateStr($b);

        if ( lc($TYPE) eq "desc" ) {
            $ts_b <=> $ts_a;
        }
        else {
            $ts_a <=> $ts_b;
        }
    } 

which is used as a sorting function & called via

sort { DATESORT('ASC') } @tran_dates_to_load

Everything works just fine when the functions and function calls are in the same file. When moving the function as-is to package.pm, it suddenly raises a bunch of Errors for uninitialized values.

Working with perl 5.16.3.

CodePudding user response:

$a and $b are package variables, so cannot be easily accessed across package boundaries. Demonstration:

use v5.12;

package Foo {
    sub bar {
        say "[$a]";
        say "[$b]";
        1;
    }
}

package main {
    my @x = sort { Foo::bar() } 1 .. 2;
}

You can work around that by fully referencing the variables:

use v5.12;

package Foo {
    sub bar {
        say "[$main::a]";
        say "[$main::b]";
        1;
    }
}

package main {
    my @x = sort { Foo::bar() } 1 .. 2;
}

However, this hardcodes an assumption into Foo::bar() — it now assumes it will always be called from main.

You can eliminate the hardcoding thanks to caller, though it's a little ugly:

use v5.12;

package Foo {
    sub bar {
        no strict 'refs';
        my ( $x, $y ) = map ${ caller . '::' . $_ }, qw( a b );
        say "[$x]";
        say "[$y]";
        1;
    }
}

package main {
    my @x = sort { Foo::bar() } 1 .. 2;
}

A better solution is to pass $a and $b as function arguments.

use v5.12;

package Foo {
    sub bar {
        my ( $x, $y ) = @_;
        say "[$x]";
        say "[$y]";
        1;
    }
}

package main {
    my @x = sort { Foo::bar( $a, $b ) } 1 .. 2;
}
  • Related