in reply to Using a sorting subroutine in a module with an array of arrays as parameters

A very similar problem is discussed in Inherit custom sort method from base class.

Basically, the problem is that  $a and  $b are package variables, and Perl special variables to boot (see perlvar).

When you run sort in the test.pl file, the  $a and  $b variables are being assigned values for use by the sort comparison subroutine. Assuming there is no package statement in test.pl, these special variables are in the main package, the default package in effect in the absence of any package statement; Perl is really assigning to the  $main::a and  $main::b (which can be abbreviated as  $::a and  $::b) special variables.

When you use  $a and  $b in a comparison subroutine defined in the personel package of file personel.pm, you're really accessing the  $personel::a and  $personel::b package variables. These variables have never been assigned any value. Update: This problem can be avoided by fully qualifying the package variable names as  $main::a etc. as above, or by figuring out the package of the caller and constructing a symbolic reference as mentioned in the OP.

File personel.pm:

package personel; use warnings; use strict; sub sortAge { $b->[1] <=> $a->[1]; } sub cmpAge1 { $main::b->[1] <=> $main::a->[1]; } sub cmpAge2 { $::b ->[1] <=> $::a ->[1]; } sub cmpAge3 { # slower my $callingPkg = caller; no strict 'refs'; ${"$callingPkg\::b"}->[1] <=> ${"$callingPkg\::a"}->[1]; # ${"${callingPkg}::b"}->[1] <=> ${"${callingPkg}::a"}->[1]; # also + works } return 1;

File test.pl:

use warnings; use strict; use personel; use Data::Dump; my @personnel = ( [ 'amy', 35 ], [ 'bill', 55 ], [ 'george', 28 ], [ 'jason', 71 ], ); print "doesn't work: \n"; my @not_sorted_by_age = sort personel::sortAge @personnel; dd \@not_sorted_by_age; print "\n"; print "does work: \n"; my @sorted_by_age_descending1 = sort personel::cmpAge1 @personnel; dd \@sorted_by_age_descending1; print "\n"; print "also works: \n"; my @sorted_by_age_descending2 = sort personel::cmpAge2 @personnel; dd \@sorted_by_age_descending2; print "\n"; print "also works (somewhat slower): \n"; my @sorted_by_age_descending3 = sort personel::cmpAge3 @personnel; dd \@sorted_by_age_descending3; print "\n"; print "original array (should be unchanged): \n"; dd \@personnel; print "\n";

Output:

c:\@Work\Perl\monks\polmed>perl test.pl doesn't work: Use of uninitialized value in numeric comparison (<=>) at personel.pm +line 7. Use of uninitialized value in numeric comparison (<=>) at personel.pm +line 7. Use of uninitialized value in numeric comparison (<=>) at personel.pm +line 7. Use of uninitialized value in numeric comparison (<=>) at personel.pm +line 7. Use of uninitialized value in numeric comparison (<=>) at personel.pm +line 7. Use of uninitialized value in numeric comparison (<=>) at personel.pm +line 7. Use of uninitialized value in numeric comparison (<=>) at personel.pm +line 7. Use of uninitialized value in numeric comparison (<=>) at personel.pm +line 7. [["amy", 35], ["bill", 55], ["george", 28], ["jason", 71]] does work: [["jason", 71], ["bill", 55], ["amy", 35], ["george", 28]] also works: [["jason", 71], ["bill", 55], ["amy", 35], ["george", 28]] also works (somewhat slower): [["jason", 71], ["bill", 55], ["amy", 35], ["george", 28]] original array (should be unchanged): [["amy", 35], ["bill", 55], ["george", 28], ["jason", 71]]

(Note that had you been using warnings, Perl would have given you a valuable hint as to the underlying problem. Using strict is also a good idea for a novice — and for more venerable monks as well.)

Replies are listed 'Best First'.
Re^2: Using a sorting subroutine in a module with an array of arrays as parameters
by LanX (Saint) on Oct 19, 2013 at 20:25 UTC
    great post! ++ :)

    > Using strict is also a good idea for a novice — and for more venerable monks as well.)

    ha! :)

    Well if you mean me, when testing within the debugger using warnings and strict is a bit complicated.

    EDIT:

    anyway I'm still waiting to see a use-case where an isolated cmp-routine make more sense than just putting the whole logic into a dedicated sort-routine using sort within the body.

    UPDATE

    to answer my own question, it might make sense when cascading different sort criterias

    DB<110> @records= (["amy", 35], ["bill", 35], ["george", 28], ["jaso +n", 35]) => (["amy", 35], ["bill", 35], ["george", 28], ["jason", 35]) DB<111> sub cmp_age { $::a->[1] <=> $::b->[1] } DB<112> sub cmp_name { $::a->[0] cmp $::b->[0] } DB<113> sort {cmp_age or cmp_name} @records => (["george", 28], ["amy", 35], ["bill", 35], ["jason", 35]) DB<114> sort {cmp_age or - cmp_name} @records => (["george", 28], ["jason", 35], ["bill", 35], ["amy", 35])

    Cheers Rolf

    ( addicted to the Perl Programming Language)