I found this a very worthwhile learning experience. Thanks all for the answers and remarks (and haukex for starting it).
I've gathered all versions (as some others also did) and formatted them all alike, all using the *same* *shuffled* input of -50..50
use warnings;
use strict;
use Benchmark "cmpthese";
use List::Util "shuffle";
use constant DO_CHECK => 0;
use if DO_CHECK, "Data::Compare", qw/Compare/;
my $bnd = 50;
my @in = shuffle (-$bnd .. $bnd);
my $exp = [ 0 .. $bnd, -$bnd .. -1 ];
cmpthese (-2, {
sortfirst => sub {
my @list = @in;
@list = sort { $a <=> $b } @list;
@list = ((grep { $_ >= 0 } @list), (grep { $_ < 0 } @list));
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
grepfirst => sub {
my @list = @in;
my @pos = grep { $_ >= 0 } @list;
my @neg = grep { $_ < 0 } @list;
@list = ((sort { $a <=> $b } @pos), (sort { $a <=> $b } @neg))
+;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
packunpck => sub {
my @list = map { unpack "l>", $_ } sort map { pack "l>", $_ }
+@in;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
pryrt => sub {
sub sgn { $_[0] < 0 ? -1 : 1 }
my @list = sort { (sgn ($b) <=> sgn ($a)) || ($a <=> $b) } @in
+;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
choroba => sub {
my @list = sort {
((-1, 0, 1)[$a <=> 0] <=> (-1, 0, 1)[$b <=> 0]) || ($a <=>
+ $b)
} @in;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
choroba0 => sub { # from CB
my @list = sort { (($b + .5 <=> 0) <=> ($a + .5 <=> 0))
|| ($a <=> $b) } @in;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
choroba2 => sub {
my @list = sort {
((($a <=> 0) & 3) <=> (($b <=> 0) & 3)) || ($a <=> $b)
} @in;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
eily => sub {
my @list = sort { ~$b <=> ~$a } @in;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
vr => sub { # https://www.perlmonks.org/?node_id=1229415
my @list = unpack "i*", pack "I*", sort { $a <=> $b }
unpack "I*", pack "i*", @in;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
Discipulus => sub { # https://www.perlmonks.org/?node_id=1229419
my @list = sort {$a<=>$b} @in;
push @list, shift @list until $list[0] >= 0;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
haukex3 => sub { # based on sortfirst above
my @list = sort { $a <=> $b } @in;
my $i; for (0 .. $#list) { if ($list[$_] >= 0) { $i = $_; last
+; }}
# with this module is ~5% faster:
# use List::MoreUtils::XS "firstidx";
# my $i = firstidx { $_ >= 0 } @list;
@list = (@list[$i .. $#list], @list[0 .. $i-1]);
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
johngg => sub { # https://www.perlmonks.org/?node_id=1229410
my @list = map { unpack q{xl>}, $_ } sort map {
my $neg = $_ < 0 ? 1 : 0; pack q{Cl>}, $neg, $_; } @in;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
Corion => sub { # based on Corion's idea in the CB
my @list = sort { $a >= 0 && $b < 0 ? -1
: ($a < 0 && $b >= 0 ? 1 : $a <=> $b) } @in;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
swl2 => sub {
my @list = sort {$a<=>$b} @in;
my $i = 0;
$i++ while ($list[$i] < 0);
push @list, splice @list, 0, $i;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
tybalt89 => sub {
my $high = my @list = sort { $a <=> $b } @in;
my $mid = my $low = 0;
$list[$mid = $low + $high >> 1] < 0 ?
($low = $mid + 1) : ($high = $mid) while $low < $high;
push @list, splice @list, 0, $low;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
GrepPos => sub {
my @list = @in;
my $pos = grep { $_ >= 0 } @list;
@list[$pos .. $#list, 0 .. $pos - 1] = sort { $a <=> $b } @lis
+t;
DO_CHECK and (Compare (\@list, $exp) or die "@list");
},
# hdb => sub {
# my @list = sort { $a * $b > 0 ? $a <=> $b : $b <=> $a } @in;
# DO_CHECK and (Compare (\@list, $exp) or die "@list");
# },
});