perlでリストをユニークにする

perlでリストをユニークにする方法です。


perlには配列などをユニークにするメソッドがありません。
なので自分でなんとかして処理するしかありません。

方法1

簡単な方法はリストをハッシュのキーに詰めていき、
最後にハッシュのキーをリストとして取得する方法。

uniq.pl

my @list = qw|a b a b c a d|;
print "list  : @list \n";

my %temp;
for (@list) {
    $temp{$_} = 1;
}
my @result = keys %temp;

print "result: @result \n";

実行するとユニークになっています。
が、ハッシュのキーは順番が保障されないため、
入力のリストと順番が合っていません。

# perl uniq.pl
list  : a b a b c a d
result: c a b d
方法2

ハッシュを配列スライスすれば同じことがループせずにできます。
uniq2.pl

my @list = qw|a b a b c a d|;
print "list  : @list \n";

my %temp;
@temp{@list} = 1;
my @result = keys %temp;

print "result: @result \n";

実行するとユニークになっています。
が、同様に順番は保障されません。

# perl uniq2.pl
list  : a b a b c a d
result: c a b d
方法3

簡単で順番も保障されて、そこそこ性能がいい方法がこちら。
uniq3.pl

my @list = qw|a b a b c a d|;
print "list  : @list \n";

my %temp;
my @result = grep !$temp{$_}++, @list;

print "result: @result \n";


結果はユニークになっていて、順番も保障されます。

# perl uniq3.pl
list  : a b a b c a d
result: a b c d

メソッドにしておいても便利です。

sub uniq {
    my ($class, @array) = @_;
    my %appearance;
    my @unique = grep !$appearance{$_}++, @array;
    return @unique;
}


どういう処理になっているかというと、
listの一番目のaが処理されるとき、
$temp{a}はundefとなります。

undefはfalseとなるので、!でtrueとして判断されます。
@resultの格納対象となります。
その後、++でインクリメントされますが、
undefをインクリメントすると1となります。

2番目のbが処理されるときも、同様の判定が行われ、
@resultの格納対象になります。

つぎに3番目のa(2回目)が来たとき。
$temp{a}は1番目のaで処理されているので値は1になります。
1はtrueなので、それが!でfalseになり、
@resultの格納対象外になります。
その後、++でインクリメントされて2になります。

つまり、同じ値が2回以上来た場合は
常にfalseとなり、@resultの格納対象外となります。


こちらからは以上です