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

How can I sort a set of strings by the season they represent? My comparison function apparently works, but when I actually use it, it only returns the input array:
use warnings; use strict; # The terms are to be sorted in this order: # SM (summer) # AU (autumn) # WI (winter) # SP (spring) sub by_term { my($t1, $t2)=@_; if ($t1 eq 'SM') { return $t2 eq 'SM' ? 0 : -1 } if ($t1 eq 'AU') { return 1 if $t2 eq 'SM'; return $t2 eq 'AU' ? 0 : -1 } if ($t1 eq 'WI') { return 1 if $t2 =~ /SM|AU/; return $t2 eq 'WI' ? 0 : -1 } if ($t1 eq 'SP') { return $t2 eq 'SP' ? 0 : 1 } } my @a = @b = qw(SM AU WI SP); # by_term() returns the right integer for each possible comparison for my $a (@a) { for my $b (@b) { warn "$a - $b" . by_term($a, $b); } } # however, the test suite yields only the input array! my @try = ( [qw(WI AU SM SP)], [qw(SP AU SM WI)], [qw(AU WI SP SM)] ); for my $try (@try) { my @sort = sort by_term @$try; warn "@sort"; }

script output

[tbrannon@Ghostdc7600] [/cygdrive/l/Temp-Erased_On_Monthend] perl by_t +erm.pl SM - SM0 at by_term.pl line 23. SM - AU-1 at by_term.pl line 23. SM - WI-1 at by_term.pl line 23. SM - SP-1 at by_term.pl line 23. AU - SM1 at by_term.pl line 23. AU - AU0 at by_term.pl line 23. AU - WI-1 at by_term.pl line 23. AU - SP-1 at by_term.pl line 23. WI - SM1 at by_term.pl line 23. WI - AU1 at by_term.pl line 23. WI - WI0 at by_term.pl line 23. WI - SP-1 at by_term.pl line 23. SP - SM1 at by_term.pl line 23. SP - AU1 at by_term.pl line 23. SP - WI1 at by_term.pl line 23. SP - SP0 at by_term.pl line 23. WI AU SM SP at by_term.pl line 35. SP AU SM WI at by_term.pl line 35. AU WI SP SM at by_term.pl line 35.

update:Khisanth figured it out... sort() calls the sorting routine with $a, $b not in the normal fashion...

<Khisanth> yeah, replacing my ($t1, $t2) = @_; with my ($t1, $t2) = ($a,$b);
	   gives SM AU WI SP

Replies are listed 'Best First'.
Re: Sorting by season
by ikegami (Patriarch) on Oct 23, 2009 at 20:06 UTC
    Your custom sorter should use $a and $b as the inputs. If you wish for the arguments to be passed via @_, you need to use the ($$) prototype. I believe this is slower.

    In fact, your whole approach is complex and slow. Fix:

    my %term_order; @term_order{qw( SM AU WI SP )} = 1..4; my @try = ( [qw( WI AU SM SP )], [qw( SP AU SM WI )], [qw( AU WI SP SM )] ); for my $try (@try) { my @sort = sort { $term_order{$a} <=> $term_order{$b} } @$try; print "@sort\n"; }
    SM AU WI SP SM AU WI SP SM AU WI SP
Re: Sorting by season
by Corion (Patriarch) on Oct 23, 2009 at 20:03 UTC

    (re)Read sort. Either your subroutine must use $a and $b or it must have a prototype of ($$). Yours does not do the first and does not have the latter.

Re: Sorting by season
by toolic (Bishop) on Oct 23, 2009 at 20:08 UTC
    Your code does not compile with use strict;. It is misleading to post code which you are not using.
Re: Sorting by season
by metaperl (Curate) on Oct 23, 2009 at 20:13 UTC
    Khisanth came up with a nice cleaner way of specifying the sort:
    my %seasons; @seasons{qw(SM AU WI SP)} = (1..4); sub by_hash { $seasons{$a} <=> $seasons{$b}; }