Perlメモ - "sort { $b cmp $a; }"と"reverse"関数の違い

本サイトで用いているPerlスクリプトにバグがあり、調べたところリストのソート方法に誤りがあったことがわかりました。初歩的なミスとは思いますが、自分自身のメモであると同時に、同じ勘違いをしてしまう人もいるかと考え掲載します。

内容

本サイトのトップページでは、新しい記事ほど先頭へ表示するよう処理させているつもりでした。しかし、過去の記事のいくつかを更新したら、なぜか順番が崩れてしまいました。

各々の記事は日付を元にしたファイル名で定義されているので、readdir関数で読み込んだファイルを逆ソートして処理すれば良いと考え、以下のように記述していました。


    foreach( grep !/^\./, reverse readdir(DIR) ){   # 誤り!
        some_action();
    }

これでは期待通りに動きません。

readdir関数は、opendir関数によってオープンしたディレクトリハンドルから、単に、ディレクトリエントリを読むだけで、得られるリスト値は昇順で並んでいることなど保証していません(この順序はOSが決めているんでしょうか?)。

一方、reverse関数は、中身の値を見てそれを逆ソートしてくれるわけではありません。単にリストの要素を逆順に並べ直す、つまり、ポインタを入れ換えるだけです。ここを私は勘違いしていました。

目的の動作をさせるには、結局、sort関数を使います。sort関数にBLOCKまたはSUBNAMEを省略すると、リスト値を昇順にソートしますが、sort { $b cmp $a; } @listとすれば逆順にソートされます。


    foreach( grep !/^\./, sort { $b cmp $a; } readdir(DIR) ){
        some_action();
    }

grep関数とsort関数の記述順も気になりますが、とりあえずはこのままで、と...。

参考資料:プログラミングPerl〈VOLUME1〉 (オライリー・ジャパン)

正確には、私が持っているのはPerl5向けの第2版です。1冊5,000円もするのにこの第3版ではVOLUME1とVOLUME2の2冊になってしまいました。合わせて1万。今さら買う気しないなぁ(笑)。

上記、逆ソートについて、sort { $b cmp $a; } @listだけでなく、reverse sort @listでもまったく等価であると書かれていました。後者の方が効率が悪そうですが、まぁ、開発者本人がまったく等価と言っているのでそうなのでしょう。

Written in 2007-07-01
この記事が属しているカテゴリ: OTHER, PERL

この記事へのコメント

semollina wrote at 2008-12-08T17:38+09:00
わざわざコメントを頂戴し、ありがとうございます。
他のperlメモも拝見したいと思いリンクを探してみたのですが、うまく見つけることができませんでした。
perlに関するメモはこのページだけ...でしょうか。

# わたしの相方も「劇団四季」の壺から出てこられない人種の一人です。そのおかげで一日中いえの中ではなにかのCDが流れています。topページを見て、管理人様と不思議な縁をちらりと感じまして...。失礼いたしました。
管理者Relaxinから... at 2008-12-08T22:50:55+9:00
semollina様

> perlに関するメモはこのページだけ...でしょうか。
はい。このページだけなんです。
お手間取らせてしまったようで申し訳ありません。ちょっとサイトの構造がわかりづらいですね。

隠し機能にするつもりはなかったのですが、実は同一カテゴリ内の記事を一覧表示するモードもありまして、
http://www.relaxin-with.com/cgi-bin/Document.cgi/PERL
このURLにアクセスするとPerlに関する記事がすべて(?)表示されます。ただ前述の通り、現状はこの記事のみです...。
最初からこのURLが明示されていればsemollina様にも苦労はかけずに済んだのにと反省しております。

> 一日中いえの中ではなにかのCDが流れています。
素晴らしい家庭ですね!
semollina wrote at 2008-12-06T17:52+09:00
Relaxin様
少し前より必要に駆られて、perlについて本を読みながらぼしぼし学び始めた者です。
reverse関数についての「中身をみて...」との太字のご説明、目から鱗が落ちました。わたしもsort関数とreverse関数は並び買える基準は文字コード順かその逆かの違いというふうに思いこんでいました。
ここでのご説明を拝見して、改めて今読んでいる本を見直すと確かに「reverse関数は、引数で与えられたリストの要素を逆順に並べ替えた新しいリストを作ります」と書かれていました。
ふらふらとたどり着いた流れ者ですが、一言お礼まで。
ありがとうございます。
管理者Relaxinから... at 2008-12-06T23:47:44+9:00
semollina様

書き込みありがとうございます。
こんな些細な情報でも人さまのお役に立ったことがわかりとても嬉しいです。
サイト管理者として何よりも励みになりました。
ありがとうございます。
管理者Relaxin wrote at 2007-09-15T09:14+09:00
追加。というかまだバグがあったので...。
上記のように文字列比較する場合の比較演算子は、"cmp"です。私のRSSを出力するスクリプトでは、"<=>"を使ってしまっていました。
デバッグ環境(Windows2000, ActivePerl perl 5.8.8)ではうまく動作してしまっていましたが、サーバ環境(FreeBSD 6.1-RELEASE, Apache2.0.59, perl 5.8.8)では期待通りにソートしてくれませんでした。

この記事へコメントを書く

コメント投稿欄

[認証用画像] (左の[画像]の文字列を入力してください)