How can I sort one key ascending, another descending without using $b (comparison) $a in Perl?

StackOverflow https://stackoverflow.com/questions/9398789

  •  29-10-2019
  •  | 
  •  

Question

In Perl, I can do:

my @unit_indices = sort { 
    $units{$b}[0] <=> $units{$a}[0] 
        or
    $a cmp $b
} keys %units;

which sorts by one field (array element) descending and the other (hash key) ascending, but causes perlcritic to complain:

Forbid $b before $a in sort blocks at line X, column Y. See page 152 of PBP. (Severity: 1)

Perl Best Practices recommends using reverse instead.

But the operation would be much more comprehensible if you wrote:
@sorted_results = reverse sort @unsorted_results;

However, I haven't found a way to have subsorts that run in opposite directions.

Obviously, I can tell perlcritic to ignore this, but I'd like to know how to accomplish what I need and make perlcritic happy.

Was it helpful?

Solution

I agree with tchrist's comment, but taking your question at face value:

1) use ## no critic

my @unit_indices = sort {   ## no critic (ReverseSortBlock)
    $units{$b}[0] <=> $units{$a}[0] 
        or
    $a cmp $b
} keys %units;

2) Use a .perlcriticrc file

[-BuiltinFunctions::ProhibitReverseSortBlock]

3) Change the sense of your comparison

my @unit_indices = sort { 
    -($units{$a}[0] <=> $units{$b}[0])
        or
    $a cmp $b
} keys %units;

my @unit_indices = sort { 
    -$units{$a}[0] <=> -$units{$b}[0])
        or
    $a cmp $b
} keys %units;

OTHER TIPS

Perl::Critic at times will concern itself more with being an accurate reproduction of PBP than presenting good policy, and some of Perl Best Practices hasn't aged well. For example, the now woefully out of date Miscellanea::RequireRcsKeywords is still on by default.

Perl::Critic's policies shouldn't be treated as canon. They lack the ability to do subjective analysis to decide if the "fix" is actually going to increase complexity, particularly as the severity level drops and the benefits get narrower and narrower. BuiltinFunctions::ProhibitReverseSortBlock is a "cosmetic" level policy and falls squarely into this category.

While somebody might skip $b cmp $a and read it backwards, it is not difficult to comprehend once squarely looked at, is not worth the overhead of reversing the whole array afterward, and is certainly not worth contorting your sort block to match the limitations of the policy analysis. Their decision not to change the default behavior to limit the policy to simple sort blocks is IMO incorrect. Your sort block is obviously beyond the scope of the written policy and is only triggered because Perl::Critic's policy implementation is limited.

Just because Perl::Critic has a policy on by default doesn't mean they represent good practice nor that they should be followed blindly. Feel free to configure it to your project's tastes, running Perl::Critic at its most picky levels requires it. In order to prevent silencing perlcritic from being a habitual thing, I would recommend preferring project-wide policy decisions in .perlcritic to individually turning them off line by line.

Remember, the point is not to be perlcritic happy, the point is to write better code.

Feel free to use perlcritic, but don't be a slave to it. The whole point of having perlcritic is to be have the ability to issue warnings that have a good chance of being wrong.

It's surely trying to encourage the use of reverse sort { $a <=> $b } over sort { $b <=> $a }, but that can't be done here.

Don't make your code worse to silence a spurious warning. Sometimes, it's the warning that's wrong, in which case you should address the warning.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top