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

Hi, I have a sort sub that I want to be able to call from a different package than the one in which it is defined. The problem is that $a and $b are in the package of the caller so the sub doesn't see them. For example:
#!/usr/footprints_perl/bin/perl -- package joe; my @names = ('joe', 'aaron'); package FP; sub sortNames ($$) { print "$a vs $b\n"; return $a cmp $b; } package joe; my @sorted = sort {FP::sortNames} @names; print "sorted : @sorted\n"; __END__ vs sorted : joe aaron
I found a description of this very same problem, and a solution (use prototypes) in japhy's Resorting to Sorting tutorial (in the very last section). However, I cut and pasted his example script into a file, and it didn't work: it printed the original unsorted data.

What's the typical way to get around this? Any idea why japhy's example doesn't work?

-Joe

Replies are listed 'Best First'.
Re: sort sub in a different package
by dragonchild (Archbishop) on Apr 21, 2005 at 15:05 UTC
    Next time, turn on strict and warnings.
    use strict; use warnings; package joe; my @names = ('joe', 'aaron'); package FP; sub sortNames ($$) { my ($x, $y) = @_; print "$x vs $y\n"; return $x cmp $y; } package joe; my @sorted = sort {FP::sortNames($a,$b)} @names; print "sorted : @sorted\n";
      Now, if you read the docs on sort, you can see that you don't have to handcraft this mode, as it is built into perl:
      If the subroutine's prototype is ($$), the elements to be compared are passed by reference in @_, as for a normal subroutine. This is slower than unprototyped subroutines, where the elements to be compared are passed into the subroutine as the package global variables $a and $b (see example below). Note that in the latter case, it is usually counter-productive to declare $a and $b as lexicals.
      What a surprise, you did use a prototype. So this will work:
      my @names = qw(Brad Aaron Joseph); package FP; sub sortNames ($$) { my ($x, $y) = @_; print "$x vs $y\n"; return $x cmp $y; } package joe; my @sorted = sort FP::sortNames @names;

      Note that the sub must already be compiled, or the prototype for the sub must otherwise already be known, when this sort statement is parsed.

      Update Much to my surprise, the latter doesn't seem to be true. It works just as well, with the FP::sortNames sub defined after the sort call. Can anyone explain?

        I found nothing about that in the sort documentation, like you I imagine. I only guess that this check for the prototype is done at run-time instead of compile time, as the following modification may suggest:
        #!/usr/bin/perl -w use strict; package joe; my @names = ('joe', 'aaron'); package FP; sub sortNames ($$) { print "$_[ 0 ] vs $_[ 1 ]\n"; return $_[ 0 ] cmp $_[ 1 ]; } package joe; my @sorted = sort nonexistent @names; print "sorted : @sorted\n"; CHECK {print "here I am\n"} __END__ here I am Undefined subroutine in sort at asort.pl line 15.

        Flavio (perl -e 'print(scalar(reverse("\nti.xittelop\@oivalf")))')

        Don't fool yourself.

      Or more simply,

      #!/usr/footprints_perl/bin/perl -w use strict; package joe; my @names = ('joe', 'aaron'); package FP; sub sortNames ($$) { print "$_[ 0 ] vs $_[ 1 ]\n"; return $_[ 0 ] cmp $_[ 1 ]; } package joe; my @sorted = sort FP::sortNames @names; print "sorted : @sorted\n"; __END__ P:\test>junk joe vs aaron sorted : aaron joe

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco.
      Rule 1 has a caveat! -- Who broke the cabal?
Re: sort sub in a different package
by johnnywang (Priest) on Apr 21, 2005 at 18:57 UTC
    The following description from the Camel book (3rd edition, section 28.2) is also helpful:
    $a
    
        [PKG]This variable is used by the sort function to 
    hold the first of each pair of values to be compared 
    ($b is the second of each pair). The package for $a is the 
    same one that the sort operator was compiled in, which is 
    not necessarily the same as the one its comparison function
     was compiled into. This variable is implicitly localized 
    within the sort comparison block. Because it is a global, 
    it is exempt from use strict complaints. Because it is an
     alias for the actual array value, you might think you can
     modify it, but you shouldn't. See the sort function.