3

I have a very strange array sorting related problem in PHP that is driving me completely crazy. I have googled for hours, and still NOTHING indicates that other people have this problem, or that this should happen to begin with, so a solution to this mystery would be GREATLY appreciated!

To describe the problem/question in as few words as possible: When sorting an array based on values inside a multiple levels deeply nested array, using a foreach loop, the resulting array sort order reverts as soon as execution leaves the loop, even though it works fine inside the loop. Why is this, and how do I work around it?

Here is sample code for my problem, which should hopefully be a little more clear than the sentence above:

$top_level_array = array('key_1' => array('sub_array' => array('sub_sub_array_1' => array(1),
                                                               'sub_sub_array_2' => array(3),
                                                               'sub_sub_array_3' => array(2)
                                                              )
                                         )
                        );

function mycmp($arr_1, $arr_2)
{
    if ($arr_1[0] == $arr_2[0])
    {
        return 0;
    }
    return ($arr_1[0] < $arr_2[0]) ? -1 : 1;
}

foreach($top_level_array as $current_top_level_member)
{
    //This loop will only have one iteration, but never mind that...
    print("Inside loop before sort operation:\n\n");
    print_r($current_top_level_member['sub_array']);

    uasort($current_top_level_member['sub_array'], 'mycmp');

    print("\nInside loop after sort operation:\n\n");
    print_r($current_top_level_member['sub_array']);
}
print("\nOutside of loop (i.e. after all sort operations finished):\n\n");
print_r($top_level_array);

The output of this is as follows:

Inside loop before sort operation:

Array
(
    [sub_sub_array_1] => Array
        (
            [0] => 1
        )

    [sub_sub_array_2] => Array
        (
            [0] => 3
        )

    [sub_sub_array_3] => Array
        (
            [0] => 2
        )

)

Inside loop after sort operation:

Array
(
    [sub_sub_array_1] => Array
        (
            [0] => 1
        )

    [sub_sub_array_3] => Array
        (
            [0] => 2
        )

    [sub_sub_array_2] => Array
        (
            [0] => 3
        )

)

Outside of loop (i.e. after all sort operations finished):

Array
(
    [key_1] => Array
        (
            [sub_array] => Array
                (
                    [sub_sub_array_1] => Array
                        (
                            [0] => 1
                        )

                    [sub_sub_array_2] => Array
                        (
                            [0] => 3
                        )

                    [sub_sub_array_3] => Array
                        (
                            [0] => 2
                        )

                )

        )

)

As you can see, the sort order is "wrong" (i.e. not ordered by the desired value in the innermost array) before the sort operation inside the loop (as expected), then is becomes "correct" after the sort operation inside the loop (as expected).

So far so good.

But THEN, once we're outside the loop again, all of a sudden the order has reverted to its original state, as if the sort loop didn't execute at all?!?

How come this happens, and how will I ever be able to sort this array in the desired way then?

I was under the impression that neither foreach loops nor the uasort() function operated on separate instances of the items in question (but rather on references, i.e. in place), but the result above seems to indicate otherwise? And if so, how will I ever be able to perform the desired sort operation?

(and WHY doesn't anyone else than me on the entire internet seem to have this problem?)

PS. Never mind the reason behind the design of the strange array to be sorted in this example, it is of course only a simplified PoC of a real problem in much more complex code.

4

2 に答える 2

3

あなたの問題は、PHP が foreach コンストラクトで「値」を提供する方法を誤解していることです。

foreach($top_level_array as $current_top_level_member)

変数 $current_top_level_member は、配列内の値のコピーであり、$top_level_array 内への参照ではありません。したがって、すべての作業はコピーで発生し、ループが完了すると破棄されます。(実際には $current_top_level_member 変数にありますが、$top_level_array は変更を認識しません。)

代わりに参照が必要です:

foreach($top_level_array as $key => $value)
{
    $current_top_level_member =& $top_level_array[$key];

編集:

foreach余分な割り当てを避けるために、参照表記 (air4x への帽子のヒント) を使用することもできます。オブジェクトの配列を操作している場合、それらはすでに参照によって渡されていることに注意してください。

foreach($top_level_array as &$current_top_level_member)

PHP がデフォルトで参照ではなくコピーを使用する理由についての質問に答えると、それは単純に言語の規則によるものです。&プレフィックスが使用されていない限り、スカラー値と配列は値によって割り当てられ、オブジェクトは常に参照によって割り当てられます ( PHP 5以降)。そしてそれは、オブジェクト以外のすべてのもののコピーを扱う方が一般的には良いという一般的なコンセンサスによるものと思われます。しかし、あなたが期待するほど遅くはありません。PHP は、コピー オン ライトと呼ばれるレイジー コピーを使用します。これは、実際には読み取り専用の参照です。最初の書き込みで、コピーが作成されます。

PHP は、変更されるまで実際には変数のコピーを作成しない遅延コピー メカニズム (コピー オン ライトとも呼ばれます) を使用します。

ソース: http://www.thedeveloperday.com/php-lazy-copy/

于 2012-10-07T03:03:57.767 に答える
1

&前に追加$current_top_level_memberして、元の配列の変数への参照として使用できます。次に、元のアレイに変更を加えます。

foreach ($top_level_array as &$current_top_level_member) {
于 2012-10-07T09:46:50.067 に答える