polmed has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

Basically I'm trying to use a sorting function from a module as i want to sort an array of arrays. I've stuck on this step for about 3 hrs, and just can't get the array of arrays to sorted as I expected.

personel.pm: package personel; use Data::Dumper; sub sortAge { $b->[1] <=> $a->[1]; } return 1;

in another file,

#under test.pl @age = ( @f1 = (amy,35), @f2=(bill,55), @f3=(george,28), @f4=(jason,71 +)); @age = sort personal::sortAge(\@age);

In a stand-alone program where subroutine is together with @age, I can simply do

@age = sort sortAge(@age);

and the expected result is like
@age = ( @f4=(jason, 71), @f2=(bill, 55), @f1=(amy, 35), @f3=(george, +28))
but when i put the subroutine in a module, it doesn't get sorted.

I also tried

 personel::sortAge(@age)

as passing directly without a reference, but the Dumper print only 1 value, as if the @age didn't get pass in successfully. In order to pass an array as a parameter to a subroutine of a module, pass by reference is needed. So I'm really confused now as how to get the @age to sort properly.

I read online that you can do something like

my @array = caller; ${$"{array}::b"}->[1] <=> ${$"{array}::a"}->[1]
but I cannot understand the syntax at all.</code>

So....please help!!! Thanks in advance!

Replies are listed 'Best First'.
Re: Using a sorting subroutine in a module with an array of arrays as parameters
by LanX (Saint) on Oct 18, 2013 at 22:40 UTC
    @age = ( @f1 = (amy,35), @f2=(bill,55), @f3=(george,28), @f4=(jason,71 +)); @age = sort personal::sortAge(\@age);

    this is not what you want¹ ... anonymous arrays are written in brackets.

    you rather meant:

    DB<150> @age => (["amy", 35], ["bill", 55], ["george", 28], ["jason", 71]) DB<151> sort personel::sortAge @age => (["jason", 71], ["bill", 55], ["amy", 35], ["george", 28])

    your syntax produced a flat list, not a nested AoA.

    DB<153> @age = ( @f1 = (amy,35), @f2=(bill,55), @f3=(george,28), @f4 +=(jason,71)); => ("amy", 35, "bill", 55, "george", 28, "jason", 71)

    see perldsc for details.

    If I were you I would define sortAge() to sort itself

    DB<157> @age = ( ["amy", 35], ["bill", 55], ["george", 28], ["jason" +,71]); => (["amy", 35], ["bill", 55], ["george", 28], ["jason", 71]) DB<158> sub personel::sortAge { sort { $b->[1] <=> $a->[1] } @_} DB<159> personel::sortAge @age => (["jason", 71], ["bill", 55], ["amy", 35], ["george", 28])

    otherwise better rename your sortAge to cmpAge, cause your code doesn't "sort" it "compares".

    Cheers Rolf

    ( addicted to the Perl Programming Language)

    ¹) and the code you posted before updating wasn't even valid perl.

Re: Using a sorting subroutine in a module with an array of arrays as parameters
by AnomalousMonk (Archbishop) on Oct 19, 2013 at 20:18 UTC

    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.)

      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)