hnwの日記

自然順ソートを行うPHPワンライナー

PHPの豊富すぎる組み込みの関数の中でも、natsort関数はかなり高機能な関数の一つだと思います。他の言語なら本体に組み込まれることは有り得ないのではないでしょうか。

natsort ― "自然順"アルゴリズムで配列をソートする


説明


bool natsort ( array &$array )


この関数は、人間が行うような手法でアルファベットまたは数字の文字列の順番を キー/値の関係を保持したままソートします。 これは、"自然順(natural ordering)"と呼ばれているものです。 このアルゴリズムと (sort() を用いた) 通常のコンピュータ文字列ソートアルゴリズムの違いを示す例を以下に示します。


http://docs.php.net/manual/ja/function.natsort.php


今回のお題は、このnatsortを使ったワンライナーです。


自然順ソートが何なのかはnatsort() の例などを参考にしてください。ざっくり言うと、文字列の辞書順ソートっぽいんだけど、数値っぽい部分は数値として比較してソートするよ、というものです。


例えばPHPのバイナリをバージョン順に並べたくなったとしましょう。しかし、実は最近登場したPHP 5.2.10がクセ者です。バージョン番号を辞書順にソートすると5.2.1の次が5.2.10になってしまうのです。

$ ls bin/php-5*
bin/php-5.0.0
bin/php-5.0.1
bin/php-5.0.2
bin/php-5.0.3
bin/php-5.0.4
bin/php-5.0.5
bin/php-5.1.0
bin/php-5.1.1
bin/php-5.1.2
bin/php-5.1.3
bin/php-5.1.4
bin/php-5.1.5
bin/php-5.1.6
bin/php-5.2.0
bin/php-5.2.1
bin/php-5.2.10
bin/php-5.2.2
bin/php-5.2.3
bin/php-5.2.4
bin/php-5.2.5
bin/php-5.2.6
bin/php-5.2.7
bin/php-5.2.8
bin/php-5.2.9
bin/php-5.3.0RC2
bin/php-5.3.0RC4


sortコマンドでうまく並べ替えられるかと思ったのですが、僕にはできませんでした。こんなときこそPHPのnatsort関数の出番です。

$ ls bin/php-5* | php -r '$lines=file("php://stdin");natsort($lines);echo implode("",$lines);'
bin/php-5.0.0
bin/php-5.0.1
bin/php-5.0.2
bin/php-5.0.3
bin/php-5.0.4
bin/php-5.0.5
bin/php-5.1.0
bin/php-5.1.1
bin/php-5.1.2
bin/php-5.1.3
bin/php-5.1.4
bin/php-5.1.5
bin/php-5.1.6
bin/php-5.2.0
bin/php-5.2.1
bin/php-5.2.2
bin/php-5.2.3
bin/php-5.2.4
bin/php-5.2.5
bin/php-5.2.6
bin/php-5.2.7
bin/php-5.2.8
bin/php-5.2.9
bin/php-5.2.10
bin/php-5.3.0RC2
bin/php-5.3.0RC4


期待通りの結果が得られました。しかもワンライナーも十分短いと思います(自画自賛)。


同じことを他の言語でやろうと思ったら、ワンライナーでやる気がしないレベルなのではないでしょうか。PHPならではのワンライナーと言えそうです。

で、これ何に使うの?

実は、「phpallコマンドでPHPの全バージョンの挙動を試す」で紹介したphpallコマンドが、PHP 5.2.10をうまく扱えないという問題が発覚しました。そこで今回のワンライナーが必要になったというわけです。

$ phpall 'var_dump(array_unique(array("01","1")));'
php-5.0.0: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.0.1: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.0.2: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.0.3: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.0.4: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.0.5: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.1.0: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.1.1: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.1.2: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.1.3: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.1.4: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.1.5: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.1.6: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.2.0: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.2.1: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.2.2: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.2.3: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.2.4: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.2.5: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.2.6: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.2.7: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.2.8: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.2.9: array(1) { [0]=> string(2) "01" }
php-5.2.10: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }
php-5.3.0RC2: array(1) { [0]=> string(2) "01" }
php-5.3.0RC4: array(2) { [0]=> string(2) "01" [1]=> string(1) "1" }


無事、正しい順番で表示できるようになりました。この修正は既にgithubhnw's phpall at master)に反映してあります。


それにしても相変わらずphpallは便利なコマンドですよね!phpallの話は真顔で言えば言うほどネタ扱いされるのが不思議です。