読者です 読者をやめる 読者になる 読者になる

HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

配列のソート関数いろいろ

PHP

あるとき、次のようなコードを見た。

<?php
    $data1 = array('ValA=4', 'ValA=1', 'ValA=2', 'ValA=5', 'ValA=3');
    $data2 = array('ValB=4', 'ValB=1', 'ValB=2', 'ValB=5', 'ValB=3');
    arsort($data1);
    foreach ($data1 as $key => $val) {
        print('('.$val.', '.$data2[$key].')<br />');
    }
?>

2つの配列の1番目、2番目、‥の要素がそれぞれ対応するデータとなる(例えば、$data1が本のタイトル、$data2がその本の作者名、とか)。
一見したとき、「$data1 だけソートしたらダメじゃん」と思ったらしい。
が、これを実行すると次のような、コード記述者が期待する結果となる(ブラウザでの見た目(謎))。

(ValA=5, ValB=5)
(ValA=4, ValB=4)
(ValA=3, ValB=3)
(ValA=2, ValB=2)
(ValA=1, ValB=1)

関数arsortは「連想配列において各配列のキーと要素との関係を維持しつつソートを行います」と説明されているのだが、上記のような普通の配列(に見えるもの)も実は添え字(0, 1, 2, ‥)をキーとする連想配列と同じで、arsortを使うと添え字と要素の関係を維持したまま逆順にソートされる。
つまり、任意の操作を施した配列について説明するとき、「$data[0] は配列の先頭の要素を参照します」という説明は厳密には間違いということになるらしい。
そのことを検証するついでに、ソート関数を一通り試してみる(natsort, natcasesort, usort, uasort, uksortは除く)。

<?php
    function printArray($name, $data) {
        print('<pre>'."${name}\n");
        foreach ($data as $key => $val) {
            printf('    %-12s[%d]=%s'."\n", $name, $key, $val);
        }
        print('        data[0] = '.$data[0]);
        print('</pre>');
    }

    $data = array('ValA=4', 'ValA=1', 'ValA=2', 'ValA=5', 'ValA=3');
    sort($data);
    printArray('sort', $data);

    $data = array('ValA=4', 'ValA=1', 'ValA=2', 'ValA=5', 'ValA=3');
    rsort($data);
    printArray('rsort', $data);

    $data = array('ValA=4', 'ValA=1', 'ValA=2', 'ValA=5', 'ValA=3');
    asort($data);
    printArray('asort', $data);

    $data = array('ValA=4', 'ValA=1', 'ValA=2', 'ValA=5', 'ValA=3');
    arsort($data);
    printArray('arsort', $data);

    $data = array('ValA=4', 'ValA=1', 'ValA=2', 'ValA=5', 'ValA=3');
    ksort($data);
    printArray('ksort', $data);

    $data = array('ValA=4', 'ValA=1', 'ValA=2', 'ValA=5', 'ValA=3');
    krsort($data);
    printArray('krsort', $data);
?>

結果は次のようになる。

sort
    sort        [0]=ValA=1
    sort        [1]=ValA=2
    sort        [2]=ValA=3
    sort        [3]=ValA=4
    sort        [4]=ValA=5
        data[0] = ValA=1

rsort
    rsort       [0]=ValA=5
    rsort       [1]=ValA=4
    rsort       [2]=ValA=3
    rsort       [3]=ValA=2
    rsort       [4]=ValA=1
        data[0] = ValA=5

asort
    asort       [1]=ValA=1
    asort       [2]=ValA=2
    asort       [4]=ValA=3
    asort       [0]=ValA=4
    asort       [3]=ValA=5
        data[0] = ValA=4

arsort
    arsort      [3]=ValA=5
    arsort      [0]=ValA=4
    arsort      [4]=ValA=3
    arsort      [2]=ValA=2
    arsort      [1]=ValA=1
        data[0] = ValA=4

ksort
    ksort       [0]=ValA=4
    ksort       [1]=ValA=1
    ksort       [2]=ValA=2
    ksort       [3]=ValA=5
    ksort       [4]=ValA=3
        data[0] = ValA=4

krsort
    krsort      [4]=ValA=3
    krsort      [3]=ValA=5
    krsort      [2]=ValA=2
    krsort      [1]=ValA=1
    krsort      [0]=ValA=4
        data[0] = ValA=4

各ソート関数の仕様はドキュメント等を見てもらうとして(謎)、「キーと要素の関係を維持しつつ」と書かれているソート関数では「$data[0]」は配列の先頭の要素にはなっていないことがわかる。
(冒頭のコードも含めて、)この辺の感覚は、連想配列を扱える言語をやってこなかった自分にはちょっと違和感があるので注意が必要かも、と思ったらしい。