Lots of discussion has ensued since I checked out yesterday, but the most generic treatment I can think of is essentially pre-computing the comparison strings in two passes, so that you can be sure that your arguments are properly conditioned:
#!/usr/bin/perl
use strict;
use warnings;
my @input = (
['blah', 'asdf', 'foo', 'bar'],
['two'],
['zzz', 'def', 'ghi'],
['one'],
['mmm', 'def', 'ghi'],
['qqq', 'xyz', 'aaa'],
);
my @list = do {
my $max_array = 0;
my $max_word = 0;
for (@input) {
$max_array = @$_ if @$_ > $max_array;
for (@$_) {
$max_word = length if length > $max_word;
}
}
my $digit = 1 + int log($max_array)/log(10);
my $format = "%$digit.d" . ("%-${max_word}s") x $max_array;
my %cache = map {$_ => sprintf $format, 0+@$_, reverse(@$_), (
+'') x $max_array} @input;
sort {$cache{$a} cmp $cache{$b}} @input;
};
$" = "', '";
print "['@$_']\n" for @list;
My measurement approach means that it's no longer sensitive to delimiter choice, but of course this is a conservative approach to that since it assumes one $max_word for all terms. The empty string padding in the sprintf is just to silence warnings.
If you want a numerical sorting for elements in place of lexical sorting, you could swap "%-${max_word}s" in the format constructor to "%${max_word}.d" More complex the pattern, the more complex the construction - you can see how impressively the code exploded for just these changes. You could even support a mixed mode by flopping between "%-${max_word}s" and "%${max_word}.d" depending on looks_like_number EXPR.
#11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.
|