1. ホーム
  2. スクリプト・コラム
  3. パール

Perlの基本的な配列のソート方法

2022-01-03 15:07:31

この記事では、Perlで文字列や数値の配列をソートする方法について学びます。

Perlにはsortという組み込み関数があり、間違いなく配列をソートすることができます。最も単純な形では、配列を渡すと、ソートされた要素の配列が返されます。sort = sort @original。

ASCIIコードに基づくソート

コピーコード コードは以下の通りです。

#! /usr/bin/perl
use strict;
use warnings;
use 5.010;
use Data::Dumper qw(Dumper);
my @words = qw(foo bar zorg moo);
say Dumper \@words;
my @sorted_words = sort @words;
say Dumper \@sorted_words;

上記の例では、次のように表示されます。
コピーコード コードは以下の通りです。

$VAR1 = [
        'foo',
        'bar',
        'zorg',
        'moo'
      ];
$VAR1 = [
        'bar',
        'foo',
        'moo',
        'zorg'
      ];

最初の出力はソート前の配列、2番目はソート後の配列を表しています。

これは最も単純なケースですが、あなたが望むものとは異なるかもしれません。例えば、ある単語が大文字で始まる場合はどうでしょう?

コピーコード コードは以下の通りです。

my @words = qw(foo bar Zorg moo);

sorted_namesの結果は、次のようになります。
コピーコード コードは以下の通りです。

$VAR1 = [
        'Zorg',
        'bar',
        'foo',
        'moo'
      ];

大文字で始まる単語が先に来ていることがわかると思います。これは、sort がデフォルトで ASCII テーブルに従ってソートされ、すべての大文字が小文字の前に来るからです。

比較機能

Perlのソートの仕組みは、元の配列の2つの要素それぞれについて、左側の値を変数$aに、右側の値を変数$bに入れるという反復処理を行います。そして、compare関数を呼び出します。compare関数"は、$aの内容が左であれば1、$bの内容が左であれば-1、両者が同じであれば0を返します。

通常は比較関数を見ずに、sortがASCIIテーブルと値を比較しますが、必要であれば明示的に記述することができます。

コピーコード コードは以下の通りです。

sort { $a cmp $b } @words;

このコードは、ブロックなしで@wordsを並べ替えるのと同じ効果を得ることができます。

ここで、perlがデフォルトで比較関数としてcmpを使用していることがわかります。これは、cmpがここで必要なことを正確に実行してくれるからです。2つの文字列の値を比較し、左の引数が右の引数より小さければ1、左の引数が右の引数より大きければ-1、そして等しければ0を返します。

アルファベット順

文字列の大文字と小文字を無視して並べ替えたい場合、つまり一般にアルファベット順と呼ばれるものは、次の例のようにすることができます。

コピーコード コードは以下の通りです。

my @sorted_words = sort { lc($a) cmp lc($b) } @words;

ここでは、比較のために、lc関数を呼び出して、引数の小文字版を返しています。そして、cmpはこれらの小文字版を比較して、元の文字列の中で誰が1番目で、誰が2番目かを判断する。

その結果は

コピーコード コードは以下の通りです。

$VAR1 = [
        'bar',
        'foo',
        'moo',
        'Zorg'
      ];

Perl による値の並べ替え

数値配列に対して sort by default を使用すると、期待した結果にならない場合があります。

コピーコード コードは以下の通りです。

my @numbers = (14, 3, 12, 2, 23);
my @sorted_numbers = sort @numbers;
say Dumper \@sorted_numbers;
$VAR1 = [
        12,
        14,
        2,
        23,
        3
     

考えてみれば、これは驚くべきことではない。compare関数は12と3を見たとき、文字列で比較します。つまり、2つの文字列 "1" と "3" の最初の文字を比較するわけです。ASCIIテーブルでは、"1"が"3"の前に来るので、文字列 "12" は文字列 "3" の前に来ることになるのです。

Perlは、あなたがこれらの値を数字でソートしたいことを魔法のように推測するわけではありません。

2つの値を数値で比較する比較関数を書くことはできますが。しかしここでは、2つの引数を数値で比較して1、-1、0を返す<=>(スペースシップ演算子としても知られている)を使っています。

コピーコード コードは以下の通りです。

my @sorted_numbers = sort { $a <=> $b } @numbers;

その結果は
コピーコード コードは以下の通りです。

$VAR1 = [
        2,
        3,
        12,
        14,
        23
      ];