Letter_Number の組み合わせの配列が与えられた場合、最初に文字で並べ替え、次に数字で並べ替えるにはどうすればよいですか。
( B_5 A_11 C_0 A_10 A_1 )
取得するため:
( A_1 A_10 A_11 B_5 C_0 )
?
デフォルトでは、sort は字句的に比較します。したがって、数値を 010 > 1 および 2 < 11 の実際の数値として扱いたい場合は、次のようになります。
my @list = qw(B_5 A_11 C_0 A_10 A_1);
my @sorted = sort char_then_num @list;
sub char_then_num {
my ($a_char, $a_num) = split '_', $a;
my ($b_char, $b_num) = split '_', $b;
return $a_char cmp $b_char
||
$a_num <=> $b_num;
}
use Sort::Key::Natural qw( natsort );
my @sorted = natsort @data;
または、モジュールを避けたい場合は、
my @sorted =
map $_->[0],
sort { $a->[1] cmp $b->[1] || $a->[2] <=> $b->[2] }
map [ $_, split /_/ ],
@data;
CPAN モジュール Sort::Naturally を試してください。
簡単で効率的:
my @list = qw(B_5 A_11 C_0 A_10 A_2);
my @sorted = map $_->[0], sort {$a->[1] cmp $b->[1] or $a->[2] <=> $b->[2]} map [$_, split'_'], @list;
編集:
シュワルツ変換を使用すると、非常に小さな値でも大きな違いが生じます。
#!/usr/bin/perl
use Benchmark qw( cmpthese );
our $chars = join("","A".."Z");
sub genlist
{
my $count = shift;
return map join("_", substr($chars, rand(26),1), int(rand(100))),
1 .. $count;
}
sub nostcmp {
my ($a_char, $a_num) = split '_', $a;
my ($b_char, $b_num) = split '_', $b;
return $a_char cmp $b_char
||
$a_num <=> $b_num;
}
sub nost {
return sort nostcmp @_;
}
sub st {
return
map $_->[0],
sort { $a->[1] cmp $b->[1] || $a->[2] <=> $b->[2] }
map [ $_, split '_' ],
@_;
}
my %tests = (
nost => 'my @sorted = nost(@list);',
st => 'my @sorted = st(@list);',
);
$_ = 'use strict; use warnings; our @list; ' . $_
for values %tests;
sub measure
{
my $count = shift;
print "Count $count:\n";
local our @list = genlist($count);
cmpthese(-3, \%tests);
print "\n";
}
measure $_ for 5,10,20,50,100;
そして結果:
Count 5:
Rate nost st
nost 82195/s -- -21%
st 103392/s 26% --
Count 10:
Rate nost st
nost 35430/s -- -34%
st 53589/s 51% --
Count 20:
Rate nost st
nost 13228/s -- -48%
st 25277/s 91% --
Count 50:
Rate nost st
nost 4157/s -- -53%
st 8935/s 115% --
Count 100:
Rate nost st
nost 1637/s -- -58%
st 3889/s 138% --
あなたが探している順序は、すでにソートのデフォルトの動作です...したがって、ソートするだけです:
my @list = qw(B_5 A_11 C_0 A_10 A_1);
my @sorted = sort @list;