再帰処理でハマった話
再帰処理を書こうとするといつも悩みます。
「こんなにループさせていいんかいな?」と。
そう思ったのがあだになった例を下記に記します。
親子関係のある配列をソートさせたい場合、下記のコードでは、pidが親IDになります。
で、これを再帰関数を使って親子関係のツリー状にしたかったのです。
<?php $eto = array( array( 'id' => 1, 'pid' => 0, 'title' => '猫', ), array( 'id' => 2, 'pid' => 4, 'title' => 'ねずみ', ), array( 'id' => 3, 'pid' => 2, 'title' => 'うし', ), array( 'id' => 4, 'pid' => 0, 'title' => 'とら', ), array( 'id' => 5, 'pid' => 4, 'title' => 'りゅう', ), array( 'id' => 6, 'pid' => 8, 'title' => 'うさぎ', ), array( 'id' => 7, 'pid' => 1, 'title' => 'ひつじ', ), array( 'id' => 8, 'pid' => 2, 'title' => 'うま', ), array( 'id' => 9, 'pid' => 4, 'title' => 'とり', ), ); function sortrecursive(&$source, $tree = array(), $pid = 0) { foreach ($source as $k=>$s){ if ($s['pid'] == $pid){ $tree[] = $s; unset($source[$k]); $tree = sortrecursive($source, $tree, $s['id']); $depth = 0; } } return $tree; } $sorted = sortrecursive($eto); var_export($sorted);
ところが上記の処理だと、
<?php array ( 0 => array ( 'id' => 1, 'pid' => 0, 'title' => '猫', ), 1 => array ( 'id' => 7, 'pid' => 1, 'title' => 'ひつじ', ), )
だけしかでてきません。!?かなりハマりました。
原因は、sortrecursiveの第一引数を参照渡しにしているためです。これは一回Treeに入ってしまえばそれ以上回す必要がないため、ループから外したいと思ってした処理です。
foreachの途中で参照渡しにしてしまっているため、処理中の配列のポインタまで渡されてしまって、渡された再帰関数の先でforeachで処理される頭の位置がずれてしまっているのでようか?
foreachする前に、reset($source)すればいいんじゃないかと思ったのですが、駄目でした。なんだかよくわかりません。とりあえず、再帰時の参照渡しをやめたらうまくいきました。