为什么 array_merge_recursive 不是递归的? [英] Why is array_merge_recursive not recursive?

查看:57
本文介绍了为什么 array_merge_recursive 不是递归的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近在我的应用程序中发现了一个由 array_merge_recursive 的意外行为引起的错误.我们来看看这个简单的例子:

$array1 = [1 =>[1 =>100,2 =>200,],2 =>[3 =>1000,],3 =>[1 =>500]];$array2 = [3 =>[1 =>500]];array_merge_recursive($array1, $array2);//返回:数组:4 [ 0 =>//...

我希望得到一个包含 3 个元素的数组:键 1、2 和 3.但是,该函数返回一个包含键 0、1、2 和 3 的数组.所以有 4 个元素,而我预期只有 3 个.当我用它们的字母等价物 (a, b, c) 替换数字,它返回一个只有 3 个元素的数组:a、b 和 c.

$array1 = ['a' =>[1 =>100,2 =>200,],'b' =>[3 =>1000,],'c' =>[1 =>500]];$array2 = ['c' =>[1 =>500]];array_merge_recursive($array1, $array2);//返回:数组:3 [ 'a' =>//...

这是(至少对我而言)意外的行为,但至少它被记录在案:

<块引用>

http://php.net/manual/en/function.array-merge-recursive.php

如果输入数组具有相同的字符串键,则这些键被合并到一个数组中,这样就完成了递归地,以便如果其中一个值是数组本身,则函数会将它与另一个数组中的相应条目合并也.但是,如果数组具有相同的数字键,则后面的value 不会覆盖原始值,但会附加.

文档对附加"的含义不是很清楚.事实证明,带有数字键的 $array1 元素将被视为索引元素,因此它们将丢失当前键:返回的数组以 0 开头.这将导致使用时出现奇怪的结果数组中的数字键和字符串键都可以,但是如果您使用了这样的不良做法,我们不要责怪 PHP.就我而言,问题是通过使用 array_replace_recursive 来解决的,这达到了预期的目的.(该函数中的'replace'表示存在则替换,否则追加;命名函数很难!)

问题 1:递归与否?

但这并不是这个问题的结束.我认为 array_*_resursive 将是一个递归函数:

<块引用>

递归是一种函数调用,其中函数调用自身.此类函数也称为递归函数.结构的递归是一种解决问题的方法,其中的解决方案问题取决于对同一问题的较小实例的解决方案.

事实证明并非如此.$array1$array2 是关联数组,$array1['c']$array2['c'] 上面例子中的索引数组只有一个元素:[1 =>500].让我们合并它们:

array_merge_recursive($array1['c'], $array2['c']);//输出:数组:2 [0 =>500, 1 =>500]

这是预期的输出,因为两个数组都有一个数字键 (1),所以第二个将附加到第一个.新数组从键 0 开始.但让我们回到第一个例子:

array_merge_recursive($array1, $array2);//输出://数组:3 [//"a" =>数组:2 [//1 =>100//2 =>200//]//"b" =>数组:1 [//3 =>1000//]//"c" =>数组:2 [//1 =>500//<-- 为什么不 0 =>500?//2 =>500//]//]

$array2['c'][1] 附加到 $array1['c'] 但它有键 1 和 2.不是 0 和 1上一个例子.处理整数键时,主数组及其子数组的处理方式不同.

问题 2:字符串或整数键有很大的不同.

在写这个问题的时候,我发现了别的东西.用子数组中的字符串键替换数字键时会变得更加混乱:

$array1 = ['c' =>['a' =>500]];$array2 = ['c' =>['a' =>500]];array_merge_recursive($array1, $array2);//输出://数组:1 [//"c" =>数组:1 [//"a" =>数组:2 [//0 =>500//1 =>500//]//]//]

因此,使用字符串键会将 (int) 500 转换为 array(500),而使用整数键则不会.

有人可以解释这种行为吗?

解决方案

如果我们退后一步观察 array_merge*() 函数在仅使用一个数组时的行为,那么我们将一瞥如何它以不同的方式处理关联数组和索引数组:

$array1 = ['k' =>[1 =>100,2 =>200,],2 =>[3 =>1000,],'f' =>'gf',3 =>[1 =>500],'99' =>'你好',5 =>'G'];var_dump(array_merge_recursive($array1));

输出:

array(6) {[k"]=>数组(2){[1]=>整数(100)[2]=>整数(200)}[0]=>数组(1){[3]=>整数(1000)}[f"]=>字符串(2)GF"[1]=>数组(1){[1]=>整数(500)}[2]=>字符串(2)嗨"[3]=>字符串(1)g"}

如您所见,它使用了所有数字键并忽略了它们的实际值,并按照遇到它们的顺序将它们返回给您.我想该函数是故意这样做的,以在底层 C 代码中保持理智(或效率).

回到你的两个数组示例,它取$array1 的值,对它们进行排序,然后然后附加$array2.

这种行为是否理智是一个完全不同的讨论......

I recently found a bug in my application caused by unexpected behaviour of array_merge_recursive. Let's take a look at this simple example:

$array1 = [
    1 => [
        1 => 100,
        2 => 200,
    ],
    2 => [
        3 => 1000,
    ],
    3 => [
        1 => 500
    ]
];
$array2 = [
    3 => [
        1 => 500
    ]
];
array_merge_recursive($array1, $array2);
//returns: array:4 [ 0 => //...

I expected to get an array with 3 elements: keys 1, 2, and 3. However, the function returns an array with keys 0, 1, 2 and 3. So 4 elements, while I expected only 3. When I replace the numbers by their alphabetical equivalents (a, b, c) it returns an array with only 3 elements: a, b and c.

$array1 = [
    'a' => [
        1 => 100,
        2 => 200,
    ],
    'b' => [
        3 => 1000,
    ],
    'c' => [
        1 => 500
    ]
];
$array2 = [
    'c' => [
        1 => 500
    ]
];
array_merge_recursive($array1, $array2);
//returns: array:3 [ 'a' => //...

This is (to me at least) unexpected behaviour, but at least it's documented:

http://php.net/manual/en/function.array-merge-recursive.php

If the input arrays have the same string keys, then the values for these keys are merged together into an array, and this is done recursively, so that if one of the values is an array itself, the function will merge it with a corresponding entry in another array too. If, however, the arrays have the same numeric key, the later value will not overwrite the original value, but will be appended.

The documentation isn't very clear about what 'appended' means. It turns out that elements of $array1 with a numeric key will be treated as indexed elements, so they'll lose there current key: the returned array starts with 0. This will lead to strange outcome when using both numeric and string keys in an array, but let's not blame PHP if you're using a bad practice like that. In my case, the problem was solved by using array_replace_recursive instead, which did the expected trick. ('replace' in that function means replace if exist, append otherwise; naming functions is hard!)

Question 1: recursive or not?

But that's not were this question ends. I thought array_*_resursive would be a recursive function:

Recursion is a kind of function call in which a function calls itself. Such functions are also called recursive functions. Structural recursion is a method of problem solving where the solution to a problem depends on solutions to smaller instances of the same problem.

It turns out it isn't. While $array1 and $array2 are associative arrays, both $array1['c'] and $array2['c'] from the example above are indexed arrays with one element: [1 => 500]. Let's merge them:

array_merge_recursive($array1['c'], $array2['c']);
//output: array:2 [0 => 500, 1 => 500]

This is expected output, because both arrays have a numeric key (1), so the second will be appended to the first. The new array starts with key 0. But let's get back to the very first example:

array_merge_recursive($array1, $array2);
// output:
// array:3 [
//  "a" => array:2 [
//    1 => 100
//    2 => 200
//  ]
//  "b" => array:1 [
//    3 => 1000
//  ]
//  "c" => array:2 [
//    1 => 500 //<-- why not 0 => 500?
//    2 => 500
//  ]
//]

$array2['c'][1] is appended to $array1['c'] but it has keys 1 and 2. Not 0 and 1 in the previous example. The main array and it's sub-arrays are treated differently when handling integer keys.

Question 2: String or integer key makes a big difference.

While writing this question, I found something else. It's getting more confusing when replacing the numeric key with a string key in a sub-array:

$array1 = [
    'c' => [
        'a' => 500
    ]
];
$array2 = [
    'c' => [
        'a' => 500
    ]
];
array_merge_recursive($array1, $array2);
// output:
// array:1 [
//  "c" => array:1 [
//    "a" => array:2 [
//      0 => 500
//      1 => 500
//    ]
//  ]
//]

So using a string key will cast (int) 500 into array(500), while using a integer key won't.

Can someone explain this behaviour?

解决方案

If we take a step back and observe how array_merge*() functions behave with only one array then we get a glimpse into how it treats associative and indexed arrays differently:

$array1 = [
    'k' => [
        1 => 100,
        2 => 200,
    ],
    2 => [
        3 => 1000,
    ],
    'f' => 'gf',
    3 => [
        1 => 500
    ],
    '99' => 'hi',
    5 => 'g'
];

var_dump( array_merge_recursive( $array1 ) );

Output:

array(6) {
  ["k"]=>
  array(2) {
    [1]=>
    int(100)
    [2]=>
    int(200)
  }
  [0]=>
  array(1) {
    [3]=>
    int(1000)
  }
  ["f"]=>
  string(2) "gf"
  [1]=>
  array(1) {
    [1]=>
    int(500)
  }
  [2]=>
  string(2) "hi"
  [3]=>
  string(1) "g"
}

As you can see, it took all numeric keys and ignored their actual value and gave them back to you in the sequence in which they were encountered. I would imagine that the function does this on purpose to maintain sanity (or efficiency) within the underlying C code.

Back to your two array example, it took the values of $array1, ordered them, and then appended $array2.

Whether or not this behavior is sane is a totally separate discussion...

这篇关于为什么 array_merge_recursive 不是递归的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆