PerlのSort関数の使い方まとめと使用例
(A) ソート関数の使用方法
ソートリスト
ソートブロック一覧
サブネームリストのソート
sortは上記のように3通りの使い方があります。LISTをソートして、ソート後のリストを返します。SUBNAME や BLOCK を無視した場合、ソートは標準的な文字列比較順 (例えば ASCII 順) で実行されます。SUBNAMEが指定された場合、それは実際には2つのリスト要素を比較し、要素がソートされる順序(昇順、定数、降順)に応じて、0より小さい、等しい、または大きい整数を返すサブファンクションの名前である。BLOCKはSUBNAMEの代わりに匿名サブファンクションとして提供することもでき、同じ効果があります。
比較される2つの要素は、一時的に変数 $a と $b に代入されます。これらは参照渡しなので、$aや$bを変更しないようにしましょう。サブファンクションを使用する場合、再帰的であってはなりません。
(b) 使用例
1. 数値順に並べる
@array = (8, 2, 32, 1, 4, 16);
print join(' ', sort {$a <=> $b} @array), "\n";
印刷結果は
と同じです。
print join(' ', sort numerically @array), "\n";
これはわかりやすく、自然数の順にソートしているだけなので、詳細は省略します。
2.1 ASCII順(辞書順ではない)でのソート
@languages = qw(fortran lisp c c++ Perl python java);
print join(' ', sort @languages), "\n";
結果を印刷します。
と同等である。
ASCII順に並べると、あまりああいうことは言わない。
数字をASCII順に並べ替えると、結果が思い通りにならないことがあることに注意してください:。
print join(' ', sort 1 . 11), "\n";
1 10 11 2 3 4 5 6 7 8 9
2.2 辞書順のソート
use locale;
@array = qw(ASCII ascap at_large atlarge A ARP arp);
@sorted = sort { ($da = lc $a) =~ s/[/W_]+//g;
($db = lc $b) =~ s/[/W_]+//g;
$da cmp $db;
} @array;
print "@sorted\n";
プリントアウトした結果は
use localeはオプションです。use localeは、生データに国際文字が含まれている場合に、コードの互換性を向上させます。use localeは、cmp, lt, le, ge, gtおよび他のいくつかの関数の操作プロパティに影響します。詳細はperllocaleのマニュアルページを参照してください。
atlargeとat_largeのソート順は同じですが、出力では順序が逆になっていることに注意してください(sortの途中にあるサブ関数がat_largeの途中にあるアンダースコアを削除しているのです)。これは、このサンプルがperl 5.005_02で実行されているために起こります。perl 5.6 より前のバージョンでは、sort 関数は同じ値を持つキーの順序を保護しませんでした。
一時変数 $_ (sort の $a と $b) は map、grep、sort のいずれによっても変更されないように保護されていることに注意してください。
このコードでは、$aや$bをs/[/W_]+/gに置き換える前に、それらを$daと$dbに再代入し、置き換えによって元の要素が変更されないようにします。
3. 降順で並べ替える
降順ソートは比較的簡単で、cmpや<=>の前後でオペランドを入れ替えるだけです。
または、中間ブロックやサブファンクションの戻り値のトークンを変更する場合。
あるいは逆関数を使う(ちょっと非効率ですが、読みやすいかもしれません)。
4. ソートに複数のキーを使用する
複数のキーでソートするには、サブファンクションでリンクされている比較演算をすべて入れればよい。主要な比較演算を最初に、マイナーな比較演算を2番目に配置します。
# An array of references to anonymous hashes
@employees = (
{ FIRST => 'Bill', LAST => 'Gates',
SALARY => 600000, AGE => 45 }
{ FIRST => 'George', LAST => 'Tester'
SALARY => 55000, AGE => 29 }
{ FIRST => 'Steve', LAST => 'Ballmer',
SALARY => 600000, AGE => 41 }
{ FIRST => 'Sally', LAST => 'Developer',
SALARY => 55000, AGE => 29 }
{ FIRST => 'Joe', LAST => 'Tester',
SALARY => 55000, AGE => 29 }
);
sub seniority {
$b->{SALARY} <=> $a->{SALARY}
or $b->{AGE} <=> $a->{AGE}
or $a->{LAST} cmp $b->{LAST}
or $a->{FIRST} cmp $b->{FIRST}
}
@ranked = sort seniority @employees;
foreach $emp (@ranked) {
print "$emp->{SALARY}/t$emp->{AGE}/t$emp->{FIRST}
$emp->{LAST}\n";
}
印刷結果は
600000 41 Steve Ballmer
55000 29 Sally Developer
55000 29 George Tester
55000 29 Joe Tester
上のコードは複雑に見えますが、実はとても簡単に理解できます。例えば、$employees[0]-> {SALARY}のようにすると、最初の匿名ハッシュのSALARYに対応する値にアクセスすることができます。つまり、上記の比較は明確です。まずSALARYの値を比較し、次にAGEの値を比較し、次にLASTの値を比較し、最後にFIRSTの値を比較しています。最初の2つの比較は降順で、最後の2つは昇順なので、混乱しないように注意してください。
5. 新しい配列の並べ替え
@x = qw(matt elroy jane sally);
@rank[sort { $x[$a] cmp $x[$b] } 0 ... $#x] = 0 ... $#x;
print "@rank\n";
印刷結果は
ちょっとわかりにくいですか?よく見ると、はっきりしますよ。0 ... $#x は @x 配列の添え字を値とするリストで、この例では 0 1 2 3 です。つまり、sort の結果は、@x の添え字をソートしたリストを返します。ソートの基準は、その添え字に対応する@xの要素のASCII順である。
sortが何を返すのか、まだ理解していないのでしょうか?まず、@xの要素のASCII順を出力してみましょう。
@x = qw(matt elroy jane sally);
print join ' ',sort { $a cmp $b } @x;
印刷結果は
それらに対応する@xの添え字は1 2 0 3なので、上記のソートは1 2 0 3のリストを返します。rank[1 2 0 3] = 0 ... $#x は単純な配列の代入操作です。
つまり、@rankの結果は(2 0 1 3)です。
6. ハッシュをキーでソートする
%hash = (Donald => Knuth, Alan => Turing, John => Neumann);
@sorted = map { { ($_ => $hash{$_}) } } sort keys %hash;
foreach $hashref (@sorted) {
($key, $value) = each %$hashref;
print "$key => $value\n";
}
印刷結果は
Donald => Knuth
John => Neumann
上のコードは難しいものではありません。 sort keys %hash は %hash による ASCII 順のキーのリストを返し、それを計算するために map を使います。ここで map は double {{}} を使っていることに注意してください。
この{}は匿名ハッシュで、つまりmapの結果は匿名ハッシュリストなんだよ、わかる?
つまり、@sorted配列の要素は個々の匿名ハッシュであり、%$hashrefでバックリファレンスすることでそのキー/バリューの値にアクセスすることができます。
7. ハッシュを値でソートする
%hash = ( Elliot => Babbage,
Charles => Babbage,
Grace => Hopper,
Herman => Hollerith
);
@sorted = map { { ($_ => $hash{$_}) } }
sort { $hash{$a} cmp $hash{$b}
or $a cmp $b
} keys %hash;
foreach $hashref (@sorted) {
($key, $value) = each %$hashref;
print "$key => $value\n";
}
印刷結果は
Elliot => Babbage
Herman => Hollerith
Grace => Hopper
ハッシュキーと違って、ハッシュ値の一意性を保証することはできない。ハッシュを値だけでソートすると、他の値を追加したり削除したりしたときに、同じ値を持つ2つの要素のソート順が変わってしまう可能性がある。安定した結果を得るためには、値をマスターソートし、キーをスレーブソートする必要があります。
ここでは、{ $hash{$a} cmp $hash{$b} or $a cmp $b } で、値によるソートとキーによるソートを2回行います。ソートの結果はキーのソートされたリストで、それをmapに渡して計算させ、匿名ハッシュリストを返します。アクセス方法は前回と同じなので、詳細は省略します。
8. ファイル内の単語をソートし、重複を削除する。
perl -0777ane '$, = "\n"; @uniq{@F} = (); print sort keys %uniq' file
この使い方は、私にもよく理解できません。
uniq{@F} = () は、ハッシュスライスを使用して、ファイル内の単語のみをキーとするハッシュを作成します。
この使い方は、意味的には $uniq{ $F[0], $F[1], ... $F[$#F] } と等価です。= ()
オプションは以下のように記述されています。
-a - auto split mode, splitting lines into @F arrays
-e - read and run scripts from the command line
-n - Iterate through the file line by line: while (<>) { ... }
$, - the output field splitter of the print function
file - the name of the file
9. 効率的なソート OrcishアルゴリズムとSchwartzian変換
sortのサブファンクションは通常、各キーに対して複数回呼び出されます。もし、sort の実行時間を非常に気にするのであれば、Orcish アルゴリズムか Schwartzian 変換を使って、各キーが一回だけ計算されるようにすることができます。
次の例では、ファイルのリストを変更日に基づいてソートしています。
@sorted = sort { -M $a <=> -M $b } @filenames;
# Orcish algorithm - create keys in the hash
@sorted = sort { ($modtimes{$a} ||= -M $a) <=>
($modtimes{$b} ||= -M $b)
} @filenames;
巧妙なアルゴリズムですね?とても賢いアルゴリズムでしょう?なぜなら、ファイルの修正日はスクリプト実行中は基本的に一定なので、一度-M操作をした後は、保存すれば完了です。
シュワルツェン変換の使い方はこうです。
sort( { $a->[1] <=> $b->[1] }
map({ [$_, -M] } @filenames)
)
);
map({ [$_, -M] } @filenames) はリストを返します。その要素は無名配列で、最初の値はファイル名、2番目の値はファイルの更新日です。
sort( { $a->[1] <=> $b->[1] }...。次に、上で生成された匿名配列リストを、ファイルの修正日を基準にソートします。
sortが返す結果は、ソートされた無名配列である。
一番外側のmap( { $_->[0] }... はシンプルで、上のsortで生成された無名配列からファイル名を取り出しています。このファイル名は、修正日ヤに基づいてソートされたもので、1ファイルにつき1回だけ実行されます -M。
これは海外のperlユーザーの間で有名な使い方であるSchwartzian変換です
関連
-
one.asp 複数のプロジェクト、関数ライブラリ、クラスライブラリを1つのバージョンのメソッドに統合
-
オンライン圧縮・解凍のためのASPコード
-
JSONデータを扱うASP実装コード
-
asp は整数の mod を受け取り、小数点以下がある場合は自動的に 1 を加算します。
-
SELECT ドロップダウンメニューで VALUE と TEXT 値を同時に取得する ASP コード
-
SharePoint Server 2019の新機能をご紹介します。
-
Perlによるディレクトリの探索とLinuxコマンドによるログの解析 コード例共有
-
Perlの単一行コメントと複数行コメントの紹介
-
perl による生物学的突然変異のランダムシミュレーションコード
-
コンストラクタでのPerlメソッド使用法入門
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
ASPでAdodbを経由して大容量ファイルをマルチスレッドでダウンロードするためのストリーム。
-
ASPで短い日付を0から長い2つの日付に書式設定する
-
aspはプロジェクトの終了時刻を計算するためにWeekday関数を使用します。
-
asp(vbscript)のカスタム関数とデフォルトのパラメータコード
-
aspで文字列を数値に変換する関数のまとめ
-
数字を漢数字(大文字の金額)に変換するASP機能
-
perl スクリプティング スタディガイド - 読書メモ
-
Perlのファイルハンドルの説明
-
重複するコンテンツ(重複する行+重複するフィールドの配列)を削除するための perl スクリプトコード
-
perl変数$/の使用方法について説明します。コンテキストが行モードのとき、$/は行を区別するものを定義します。