为什么array_diff_uassoc会比较值不匹配的键

时间:2015-04-02 19:58:05

标签: php arrays

我刚刚阅读that question关于奇怪的PHP行为,即使我可以进行更多的研究,我也无法理解它。

我假设读者已经阅读了原始问题,并且知道OP的代码块和样本,但简而言之,OP正在尝试比较这两个数组,虽然结果很好,但比较函数似乎不正常地被称为:

$chomik = new chomik('a');
$a = array(5, $chomik, $chomik, $chomik);
$b = array($chomik, 'b', 'c', 'd');
array_diff_uassoc($a, $b, 'compare');

The documentation有点模糊......但确实说明了:

  

如果第一个参数被认为分别小于,等于或大于,则比较函数必须返回小于,等于或大于零的整数。

据我了解,这意味着compare()函数应该更像这样:

function compare($a, $b) {
    echo("$a : $b<br/>");
    if($a === $b) return 0;
    else if ($a > $b) return 1;
    else return -1;
}

然而,这仍然会产生非常奇怪的结果,甚至更多的重复&#34;

1 : 0
1 : 2
3 : 1
2 : 1
3 : 2
1 : 0
1 : 2
3 : 1
2 : 1
3 : 2
0 : 0
1 : 0
1 : 1
2 : 0
2 : 1
2 : 2
3 : 0
3 : 1
3 : 2
3 : 3

面对许多疑惑,我read the compat php function,以及检查实际发生的部分很有意思:

foreach ($args[0] as $k => $v) {
    for ($i = 1; $i < $array_count; $i++) {
        foreach ($args[$i] as $kk => $vv) {
            if ($v == $vv) { // compare keys only if value are the same
                $compare = call_user_func_array($compare_func, array($k, $kk));
                if ($compare == 0) {
                    continue 3; // value should not be added to the result
                }
            }
        }
    }
    $result[$k] = $v;
}

here's the actual source(每comment

此代码执行比较函数的方式不应该输出我们看到的结果。 Foreach无法在键中来回移动(AFAIK ???),就像第一个键的顺序一样:

1 : 2
3 : 1
2 : 1

此外,如果值不匹配,它不应该检查密钥,那么为什么要检查所有这些:

1 : 2
3 : 1
2 : 1
3 : 2
etc...

源代码中最顶层的foreach()如何在键中来回循环?!

为什么仍然比较值不匹配的键?

foreach循环是否真的继续执行,即使它们已经continue d?

这是并发的一个例子吗?可以以某种方式启动call_user_func_array并实际执行比较功能的echo("$a : $b<br/>");,而不是按照它们的相同顺序执行#34;启动&#34; ??

2 个答案:

答案 0 :(得分:1)

我相信你已经找到了一个错误,我的朋友。我刚刚在您引用的问题中运行了代码,果然,它比较了不相同的值的键。但是,我想测试源代码本身是否包含错误,所以我将array_diff_uassoc的官方来源添加到他的代码顶部,在我自己的命名空间内:

<?php

namespace mine;

// Code obtained from https://pear.php.net/reference/PHP_Compat-latest/__filesource/fsource_PHP_Compat__PHP_Compat-1.6.0a3CompatFunctionarray_diff_uassoc.php.html

function array_diff_uassoc()


{

    // Sanity check

    $args = func_get_args();

    if (count($args) < 3) {

        user_error('Wrong parameter count for array_diff_uassoc()', E_USER_WARNING);

        return;

    }



    // Get compare function

    $compare_func = array_pop($args);

    if (!is_callable($compare_func)) {

        if (is_array($compare_func)) {

            $compare_func = $compare_func[0] . '::' . $compare_func[1];

        }

        user_error('array_diff_uassoc() Not a valid callback ' .

            $compare_func, E_USER_WARNING);

        return;

    }



    // Check arrays

    $array_count = count($args);

    for ($i = 0; $i !== $array_count; $i++) {

        if (!is_array($args[$i])) {

            user_error('array_diff_uassoc() Argument #' .

                ($i + 1) . ' is not an array', E_USER_WARNING);

            return;

        }

    }



    // Compare entries

    $result = array();

    foreach ($args[0] as $k => $v) {

        for ($i = 1; $i < $array_count; $i++) {

            foreach ($args[$i] as $kk => $vv) {

                if ($v == $vv) {

               //   echo ("$v\n");
                    // echo ("$vv\n");
               //   echo ("$k\n");
                    // echo ("$kk\n");
                    // die();

                    $compare = call_user_func_array($compare_func, array($k, $kk));

                    if ($compare == 0) {

                        continue 3;

                    }

                }

            }

        }



        $result[$k] = $v;

    }

    return $result;

}

class chomik {

    public $state = 'normal';
    public $name = 'no name';

    public function __construct($name) {
        $this->name = $name;
    }

    public function __toString() {
        return $this->name . " - " . $this->state;
    }
}

function compare($a, $b) {
    echo("$a : $b\n");
    if($a != $b) {
        return 0;
    }
    else return 1;
}

$chomik = new chomik('a');
$a = array(5, $chomik, $chomik, $chomik);
$b = array($chomik, 'b', 'c', 'd');
array_diff_uassoc($a, $b, 'mine\compare');

这一次,它只比较了相等值的键:

1 : 0
2 : 0
3 : 0

奇怪,是吗?

答案 1 :(得分:0)

comment powerlord开始{/ 3}

  

从自定义比较函数要求您返回-1的事实来看; 0;或者,它似乎是在两个数组之间进行比较之前或同时进行排序。

我很高兴为array_diff_uassoc()阅读actual php_array_diff() sourcethe registered function,并发现它多次使用比较功能,我错误地将其解释为foreach返回通过钥匙。


  

源代码中最顶层的foreach()如何在键中来回循环?!

它没有。只是用户提供的功能

diff_data_compare_func = php_array_user_compare;
多次使用

对数据进行排序和评估。

[...]
zend_sort((void *) lists[i], hash->nNumOfElements,
            sizeof(Bucket), diff_data_compare_func, (swap_func_t)zend_hash_bucket_swap);
[...]
while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
[...]
if (diff_data_compare_func(ptrs[0], ptr) != 0) {
[...]

  

为什么仍然比较值不匹配的键?

问题中发布的compat pear代码确实提示如果valus不匹配,则甚至不应运行compare函数,但php_array_diff()的行为不同。


  

foreach循环实际上是否继续执行,即使它们已经继续?   这是并发的一个例子吗?

无意义。如果我是你,我会编辑出来。