2

array_uintersectのドキュメントへのコメントで、コールバック関数は-1($ a <$ b)、0($ a === $ b)、または1($ a> $ b)のいずれかを返さなければならないことに注意してください。

コールバック関数の目的は、$aと$bを比較して、それらを交差点に含めるか除外するかを決定することです。では、なぜ単純なブール値の代わりに-1、0、または1を返すのでしょうか。

これが私が達成したかったことのいくつかの(機能する)サンプルコードです、私はそれがなぜそのように機能するのか興味があります。

4

4 に答える 4

4

配列入力に対して、期待以上array_uintersect()に奇妙に動作することに言及することが重要です。呼び出しにより、各エントリから、および各エントリがそれぞれ 1 回比較されることが予想されます (最初の交差が見つかった後にエントリの比較を停止する最適化を使用)。正気な人なら誰でも、 の各エントリがコールバックの引数に到達し、 の各エントリがその引数に到達することを期待するでしょう。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;
        }
    }
}
于 2016-08-08T21:05:37.417 に答える
1

usortその理由は、PHPソースやarray_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は、整数であるwhichを使用して、関数を比較します。

retval < 0 ? -1 : retval > 0 ? 1 : 0

ブール値を使用していて、変換が必要な場合は、0または1

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

これは、必要な場所のみが必要であり、他の実装では必要ないため、 boolean交差中に回避できる可能性があることを意味します。$a === $b = 0retval < 0

于 2012-11-10T02:51:47.080 に答える
1

pearの置き換えは、ブール値のみを返すコールバックを受け入れます。php 関数はそうではありません。その理由はおそらくphpの最適化です。あなたはここでこれをチェックするかもしれません

于 2012-11-10T02:54:12.510 に答える
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);
}

クイックソートは、アルゴリズムのパーティション コンポーネントを実行できるように、これらの関係に敏感です。ピボットと同じ値を持つアイテムは、ピボットの両側に隣接して配置されます。

大なり比較演算子がオブジェクト比較で機能するのは興味深いこと>です。これは文書化されていない動作です。あるコメントによると、PHP はこの比較のためにパブリック オブジェクトの値を調べます。これは、実際には現在、内部リストの議論のポイントです!

于 2012-11-10T03:12:21.517 に答える