LoginSignup
7
4

More than 5 years have passed since last update.

Perlで配列をソートした後で先頭からn個の要素がほしかった

Last updated at Posted at 2014-06-17

Perlで配列をソートした後で先頭からn個の要素がほしかった

単純にsortとspliceで書くと2行になって嫌だった

こんな感じです。

use strict;
use warnings;
use Data::Dumper;

my $array_ref = [5,8,3,10,50,4,68,41,11];
my $array_ref2 = [];

@{$array_ref2} = sort { $b <=> $a } @{$array_ref};
splice @{$array_ref2}, 5;

print Data::Dumper::Dumper $array_ref2;

exit;

何がしたいのかはわかるのですが、2行も使うような内容でもないかな、という気持ちでした。
意外とこういう処理ってよくやるような気がするので、もっとすっきりしたかったのです。

いろいろ試したら、こんな風にもかけました。

use strict;
use warnings;
use Data::Dumper;

my $array_ref = [5,8,3,10,50,4,68,41,11];
my $array_ref2 = [];

@{$array_ref2} = ( sort { $b <=> $a } @{$array_ref} )[0..4];
print Data::Dumper::Dumper $array_ref2;

結果は、両方同じで

$VAR1 = [
          68,
          50,
          41,
          11,
          10
        ];

なんとなく、すっきりしました。

速度的にはどうなの?

両方を100万回くらい回してみました。

use strict;
use warnings;
use Time::HiRes qw(gettimeofday tv_interval);

my $array_ref = [5,8,3,10,50,4,68,41,11];
my $array_ref2 = [];
my ( $t0, $t1, $et );

$t0 = [gettimeofday];
do{
    @{$array_ref2} = sort { $b <=> $a } @{$array_ref};
    splice @{$array_ref2}, 5;
} for 0..1000000;
$t1 = [gettimeofday];
$et = tv_interval($t0,$t1);
print "splice: $et\n\n";


$t0 = [gettimeofday];
do{
    @{$array_ref2} = ( sort { $b <=> $a } @{$array_ref} )[0..4];
} for 0..1000000;
$t1 = [gettimeofday];
$et = tv_interval($t0,$t1);
print "not splice: $et\n\n";

exit;
結果
splice: 1.048066

not splice: 0.759474

2行使って処理するより早いみたいですね。
ただ、100万回でこの差であればそんなに気にしないでもいいかな、と思ったり。
でも、splice使ってやるほうだと、代入する配列が巨大であれば、さらに重たくなるような気がします。

配列の要素数を増やしてみた

use strict;
use warnings;
use Time::HiRes qw(gettimeofday tv_interval);

my $array_ref = [1..100];   #面倒なので、単純に1~100の連続した数値を代入しました
my $array_ref2 = [];
my ( $t0, $t1, $et );

$t0 = [gettimeofday];
do{
    @{$array_ref2} = sort { $b <=> $a } @{$array_ref};
    splice @{$array_ref2}, 5;
} for 0..1000000;
$t1 = [gettimeofday];
$et = tv_interval($t0,$t1);
print "splice: $et\n\n";


$array_ref2 = [];

$t0 = [gettimeofday];
do{
    @{$array_ref2} = ( sort { $b <=> $a } @{$array_ref} )[0..4];
} for 0..1000000;
$t1 = [gettimeofday];
$et = tv_interval($t0,$t1);
print "not splice: $et\n\n";

exit;
結果
splice: 6.424075

not splice: 1.541562

予想通り、大幅に差が開きました。

結論

元の配列が大きい場合は、代入する前に要素数を絞れるといいですね、ということですね。

7
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4