【问题标题】:PHP Array comparison: why must the callback function for array_uintersect return -1, 0, or 1?PHP 数组比较:为什么 array_uintersect 的回调函数必须返回 -1、0 或 1?
【发布时间】:2012-11-10 01:58:52
【问题描述】:

array_uintersect 文档的评论中指出,回调函数必须返回 -1 ($a $ b)

回调函数的目的是比较 $a 和 $b 来决定是把它们包含在交集中,还是排除它们。那么为什么返回 -1、0 或 1 而不是简单的布尔值呢?

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

【问题讨论】:

  • 但是为什么需要三个值呢?
  • 考虑到-101 实际上是典型的排序标准,我认为这三个条件用于某种内部排序。
  • 对不起,我没有考虑这个特殊情况。
  • 在这种情况下,只需要两个值:一个值是否存在于另一个数组中。排序函数确实需要知道一个值是否不同,因为它更大或更小。为了避免两种不同语义的回调,它们只是让 array_uintersect() 使用与 usort() 相同的类型。
  • 也许他们只是想在数组排序函数方面保持一致。 编辑:基本上是cleong所说的;)

标签: php arrays multidimensional-array comparison


【解决方案1】:

值得一提的是,array_uintersect() 在您的数组输入上的操作甚至比人们希望的要更多奇怪。人们期望调用array_uintersect($firstArray, $secondArray, function ($a, $b) {}) 将导致来自$firstArray 的每个条目和来自$secondArray 的每个条目分别比较一次(优化在找到第一个交叉点后停止对条目的比较)。任何理智的人都会期望$firstArray 的每个条目都会出现在回调的$a 参数中,$secondArray 的每个条目都会出现在其$b 参数中。

不是的情况!信不信由你,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;
        }
    }
}

【讨论】:

  • 那么简单来说,在什么情况下array_uintersect() 会给出与上面的代码不同的结果?
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    我认为原因在于 PHP Source 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 &lt; 0 的其他实现中

    【讨论】:

    • 你不会逃脱 boolfunction myfunction($v1,$v2){ if ($v1===$v2) return 0; //if ($v1 &gt; $v2) return 1;//uncomment this to see the correct result else return -1; } $a1=array(2,4,1); $a2=array(3,1,4); print_r(array_uintersect($a1,$a2,"myfunction"));
    【解决方案4】:

    在底层,是对 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 附近和两侧。

    有趣的是,大于比较运算符&gt; 适用于对象比较,这是一种未记录的行为。根据一条评论,PHP 会查看公共对象的值以进行比较。这实际上是a discussion point on the internals list

    【讨论】:

      猜你喜欢
      • 2021-11-19
      • 1970-01-01
      • 2019-06-26
      • 2023-03-18
      • 2023-03-03
      • 2021-11-21
      • 1970-01-01
      • 2019-04-03
      • 1970-01-01
      相关资源
      最近更新 更多