PHP数组比较:为什么array_uintersect的回调函数必须返回-1,0或1?

时间:2012-11-10 01:58:53

标签: php arrays multidimensional-array comparison

array_uintersect的文档评论中注意到,回调函数必须返回-1($ a< $ b),0($ a === $ b)或1($ a> $ b)

回调函数的目的是比较$ a和$ b以确定是否将它们包含在交集中,或者将它们排除。那么为什么要返回-1,0或1而不是简单的布尔值?

这是我想要实现的一些(工作)example code,我只是好奇为什么它会这样。

4 个答案:

答案 0 :(得分:4)

值得一提的是,array_uintersect()在您的阵列输入上奇怪地运行 more ,而不是希望的。一个人希望调用array_uintersect($firstArray, $secondArray, function ($a, $b) {})将导致来自$firstArray的每个条目和来自$secondArray的每个条目进行一次比较(在找到第一个交集之后优化停止比较条目)。任何理智的人都会期望$firstArray的每个条目都会出现在回调的$a参数中,并且$secondArray的每个条目都会落入其$b参数中。< / p>

这是的情况!信不信由你,php首次拨打你的回叫电话的$a$b 都设置为来自$firstArray 的条目!您正在调用以数组交集命名的函数,但该函数还会比较各个数组中的条目,而不是简单地比较数组。真的很麻烦。

因此,array_uintersect 是以下块的替代品。用户要小心。

$intersection = [];
foreach ($firstArray as $a) {
    foreach ($secondArray as $b) {
        if (user_compare_function($a, $b) === 0) {
            $intersection[] = $a;
            break;
        }
    }
}

答案 1 :(得分:1)

我认为原因在于PHP源usortarray_uintersect以及其他类似的用户回调比较函数php_array_user_compare

xref: /PHP_5_3/ext/standard/array.c

568static int php_array_user_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */
569{
570    Bucket *f;
571    Bucket *s;
572    zval **args[2];
573    zval *retval_ptr = NULL;
574
575    f = *((Bucket **) a);
576    s = *((Bucket **) b);
577
578    args[0] = (zval **) f->pData;
579    args[1] = (zval **) s->pData;
580
581    BG(user_compare_fci).param_count = 2;
582    BG(user_compare_fci).params = args;
583    BG(user_compare_fci).retval_ptr_ptr = &retval_ptr;
584    BG(user_compare_fci).no_separation = 0;
585    if (zend_call_function(&BG(user_compare_fci), &BG(user_compare_fci_cache) TSRMLS_CC) == SUCCESS && retval_ptr) {
586        long retval;
587
588        convert_to_long_ex(&retval_ptr);
589        retval = Z_LVAL_P(retval_ptr);
590        zval_ptr_dtor(&retval_ptr);
591        return retval < 0 ? -1 : retval > 0 ? 1 : 0;
592    } else {
593        return 0;
594    }
595}

这使用retval,如果你看

,这是一个整数来比较函数
retval < 0 ? -1 : retval > 0 ? 1 : 0

如果您使用布尔值并且需要转换,则只能提供01

示例

var_dump((int) true); // 1
var_dump((int) false); // 0

这意味着您可以在交叉期间使用boolean,因为只有$a === $b = 0需要retval < 0而不是其他{{1}}

的实施

答案 2 :(得分:1)

pear replacement接受回调,只返回一个布尔值。 php函数没有。 所以原因可能是php中的优化。你可以查看here

答案 3 :(得分:1)

在幕后,是对C函数zend_qsort的调用。

if (behavior == INTERSECT_NORMAL) {
    zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), intersect_data_compare_func TSRMLS_CC);
} else if (behavior & INTERSECT_ASSOC) { /* triggered also when INTERSECT_KEY */
    zend_qsort((void *) lists[i], hash->nNumOfElements, sizeof(Bucket *), intersect_key_compare_func TSRMLS_CC);
}

Quicksort对这些关系很敏感,因此它可以执行其算法的分区组件。与 pivot 具有相同值的项目放在 pivot 的旁边和两侧。

有趣的是,大于比较运算符>适用于对象比较,这是一种无证件行为。根据一条评论,PHP会查看此对比的公共对象的值。这实际上是a discussion point on the internals list现在!