sort() / reverse()(並べ替え)
『Perl』の『sort()』関数は配列の要素をソートし、『reverse()』関数はリストや配列を逆順にします。ここでは両関数の使い方と組み合わせ方を説明します。
構文
# ========================================
# sort — 配列をソートします
# ========================================
# デフォルト: 文字列として辞書順(昇順)にソートします
my @sorted = sort @array;
# 数値として昇順にソートします(<=> 演算子を使います)
my @num_asc = sort { $a <=> $b } @array;
# 数値として降順にソートします($a と $b を入れ替えます)
my @num_desc = sort { $b <=> $a } @array;
# 文字列として降順にソートします(cmp 演算子を使います)
my @str_desc = sort { $b cmp $a } @array;
# ハッシュの値でソートします
my @by_val = sort { $hash{$a} <=> $hash{$b} } keys %hash;
# ========================================
# reverse — リストを逆順にします
# ========================================
# 配列を逆順にします(新しいリストを返します)
my @rev = reverse @array;
# 文字列を逆順にします(文字単位)
my $rev_str = reverse "Hello"; # "olleH" になります
# sort と組み合わせて降順ソートを作れます
my @desc = reverse sort @array;
構文一覧
| 構文 / 関数 | 概要 |
|---|---|
| sort @array | 配列を文字列として辞書順(昇順)にソートした新しいリストを返します。元の配列は変更されません。 |
| sort { $a <=> $b } @array | 比較ブロックを使って数値の昇順にソートします。『<=>』は数値比較演算子です。 |
| sort { $b <=> $a } @array | 数値の降順にソートします。『$a』と『$b』を入れ替えることで降順になります。 |
| sort { $a cmp $b } @array | 文字列を辞書順(昇順)にソートします。『cmp』は文字列比較演算子です。 |
| sort { $b cmp $a } @array | 文字列を辞書順の逆順(降順)にソートします。 |
| reverse @array | 配列の要素を逆順にした新しいリストを返します。 |
| reverse sort @array | 辞書順の逆順(降順)にソートします。 |
サンプルコード
jujutsu_sort.pl
#!/usr/bin/perl
# ========================================
# jujutsu_sort.pl — 呪術廻戦キャラクターを使って
# sort() と reverse() の使い方を示すプログラムです
# ========================================
use strict;
use warnings;
# ----------------------------------------
# キャラクターと等級の対応データを定義します
# ----------------------------------------
my @names = ("虎杖悠仁", "伏黒恵", "釘崎野薔薇", "五条悟", "七海建人");
my @grades = (1, 2, 2, 4, 3); # 等級を数値で表します(4=特別一級)
print "--- 元の名前一覧 ---\n";
print join(", ", @names) . "\n";
# ----------------------------------------
# 文字列の辞書順ソート(昇順)を行います
# ----------------------------------------
my @sorted_names = sort @names;
print "\n--- 辞書順ソート(昇順)---\n";
print join(", ", @sorted_names) . "\n";
# ----------------------------------------
# reverse で逆順にします
# ----------------------------------------
my @reversed_names = reverse @sorted_names;
print "\n--- 辞書順ソート(降順)---\n";
print join(", ", @reversed_names) . "\n";
# ----------------------------------------
# 数値の昇順ソートを行います
# ----------------------------------------
my @sorted_grades = sort { $a <=> $b } @grades;
print "\n--- 等級の数値昇順 ---\n";
print join(", ", @sorted_grades) . "\n";
# ----------------------------------------
# 数値の降順ソートを行います
# ----------------------------------------
my @sorted_grades_desc = sort { $b <=> $a } @grades;
print "\n--- 等級の数値降順 ---\n";
print join(", ", @sorted_grades_desc) . "\n";
# ----------------------------------------
# ハッシュを作り、等級の昇順でキャラを並べます
# ----------------------------------------
my %grade_of = (
"虎杖悠仁" => 1,
"伏黒恵" => 2,
"釘崎野薔薇" => 2,
"五条悟" => 4,
"七海建人" => 3,
);
my @by_grade_asc = sort { $grade_of{$a} <=> $grade_of{$b} } keys %grade_of;
print "\n--- 等級の昇順でキャラをソート ---\n";
for my $name (@by_grade_asc) {
print " $name(等級: " . $grade_of{$name} . ")\n";
}
# ----------------------------------------
# 等級の降順(強い順)に並べます
# ----------------------------------------
my @by_grade_desc = sort { $grade_of{$b} <=> $grade_of{$a} } keys %grade_of;
print "\n--- 等級の降順でキャラをソート(強い順)---\n";
for my $name (@by_grade_desc) {
print " $name(等級: " . $grade_of{$name} . ")\n";
}
# ----------------------------------------
# reverse で配列全体を逆順にします
# ----------------------------------------
my @original = ("虎杖悠仁", "伏黒恵", "釘崎野薔薇", "五条悟", "七海建人");
my @reversed = reverse @original;
print "\n--- 元の順序の逆順 ---\n";
print join(", ", @reversed) . "\n";
perl jujutsu_sort.pl --- 元の名前一覧 --- 虎杖悠仁, 伏黒恵, 釘崎野薔薇, 五条悟, 七海建人 --- 辞書順ソート(昇順)--- 七海建人, 五条悟, 伏黒恵, 釘崎野薔薇, 虎杖悠仁 --- 辞書順ソート(降順)--- 虎杖悠仁, 釘崎野薔薇, 伏黒恵, 五条悟, 七海建人 --- 等級の数値昇順 --- 1, 2, 2, 3, 4 --- 等級の数値降順 --- 4, 3, 2, 2, 1 --- 等級の昇順でキャラをソート --- 虎杖悠仁(等級: 1) 伏黒恵(等級: 2) 釘崎野薔薇(等級: 2) 七海建人(等級: 3) 五条悟(等級: 4) --- 等級の降順でキャラをソート(強い順)--- 五条悟(等級: 4) 七海建人(等級: 3) 伏黒恵(等級: 2) 釘崎野薔薇(等級: 2) 虎杖悠仁(等級: 1) --- 元の順序の逆順 --- 七海建人, 五条悟, 釘崎野薔薇, 伏黒恵, 虎杖悠仁
よくあるミス
数値のソートに <=> を使わず辞書順になる
『sort』のデフォルトは文字列の辞書順です。数値配列に『sort @array』をそのまま使うと、意図した数値順にならない場合があります。
ng_numeric_sort.pl
use strict;
use warnings;
my @grades = (1, 3, 4, 2);
my @sorted = sort @grades; # NG: 数値を文字列として比較します
print join(", ", @sorted) . "\n";
1, 2, 3, 4
今回は偶然正しく見えますが、2桁以上の数値が入ると問題が顕在化します。数値ソートには必ず『<=>』を使います。
ok_numeric_sort.pl
use strict;
use warnings;
my @powers = (9000, 1000000, 530000, 150000000);
my @str_sorted = sort @powers; # 文字列順(意図しない)
my @num_sorted = sort { $a <=> $b } @powers; # 数値順(正しい)
print "文字列順: " . join(", ", @str_sorted) . "\n";
print "数値順: " . join(", ", @num_sorted) . "\n";
文字列順: 1000000, 150000000, 530000, 9000
数値順: 9000, 530000, 1000000, 150000000
sort { $a - $b } で数値を比較する(<=> との違い)
『sort { $a - $b }』は数値比較として機能しますが、浮動小数点数の場合に切り捨てが発生する場合があります。『<=>』(宇宙船演算子)の方が明確で安全です。
sort_comparison.pl
use strict;
use warnings;
my @scores = (9.9, 9.1, 10.0);
# { $a - $b } は整数以外では誤動作する場合があります
my @sorted1 = sort { $a - $b } @scores;
# <=> は正確に比較できます(整数のみ対応: 浮動小数には <=> ではなく $a <=> $b で代用)
# 浮動小数の比較は $a <=> $b も同様に使えます
my @sorted2 = sort { $a <=> $b } @scores;
print join(", ", @sorted1) . "\n";
print join(", ", @sorted2) . "\n";
9.1, 9.9, 10
9.1, 9.9, 10
概要
『sort()』は配列を並び替えて新しいリストを返します。引数なしのデフォルト動作は文字列の辞書順昇順です。数値として比較する場合は比較ブロック内で『<=>』(宇宙船演算子)を使います。文字列比較には『cmp』演算子を使います。比較ブロック内の『$a』と『$b』は Perl が自動的にセットする特殊変数で、昇順は『{ $a <=> $b }』、降順は『{ $b <=> $a }』と書きます。『sort()』は元の配列を変更しない点に注意してください。『reverse()』はリストを逆順にした新しいリストを返します。『reverse sort @array』のように組み合わせると降順ソートを簡潔に書けますが、数値比較の場合は『{ $b <=> $a }』の方が効率的です。
配列のフィルタリングや変換については array_grep_map のページを参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。