comparing arrays in PHP - interesting behaviour
The first example:
$x = array("a" => 1, "b" => 2);
$y = array("b" => 1, "a" => 2);
$xLessY = ($x < $y);
$xGreaterY = ($x > $y);
var_dump($xLessY, $xGreaterY);
Result: $xLessY = true, $xGreaterY = true
The second example:
$x = array("a" => 2, "b" => 1);
$y = array("b" => 2, "a" => 1);
$xLessY = ($x < $y);
$xGreaterY =开发者_如何转开发 ($x > $y);
var_dump($xLessY, $xGreaterY);
Result: $xLessY = false, $xGreaterY = false
According to documentation on http://docs.php.net/manual/en/language.operators.comparison.php:
if key from operand 1 is not found in operand 2 then arrays are uncomparable, otherwise - compare value by value
In our case each key from array $x is present in array $y, so $x and $y are comparable. See also the example from documentation:
// Arrays are compared like this with standard comparison operators
function standard_array_compare($op1, $op2)
{
if (count($op1) < count($op2)) {
return -1; // $op1 < $op2
} elseif (count($op1) > count($op2)) {
return 1; // $op1 > $op2
}
foreach ($op1 as $key => $val) {
if (!array_key_exists($key, $op2)) {
return null; // uncomparable
} elseif ($val < $op2[$key]) {
return -1;
} elseif ($val > $op2[$key]) {
return 1;
}
}
return 0; // $op1 == $op2
}
This behaviour is really strange: $x is less than $y and at the same time $x is greater than $y (the first example) and two arrays are comparable.
I think this is because php always compares starting from the one definite side of sign '<'. I mean: for ($x < $y) php takes $x as operand 1, for ($x > $y) it takes $y as operand 1. Although I didn't find anything about this behaviour in documentation.
What are your thoughts on this?Your assumption is correct. The >
operator is parsed as
| expr '>' expr { zend_do_binary_op(ZEND_IS_SMALLER, &$$, &$3, &$1 TSRMLS_CC); }
This basically says, X > Y
is equivalent to not X < Y
, which is of course wrong when the comparison is not commutative. Consider reporting this on bugs.php.net.
I wouldn't say the bug is in $x > $y
being substituted for $y < $x
.
Sure, if you implemented $x > $y
in a way that the arguments did not exchange positions when passed to the comparison function, you would solve this particular problem. But you get another in return.
Right now you have:
$x < $y <=> cmp($x, $y) == -1
$x > $y <=> cmp($y, $x) == -1
Because the first key of the first argument is always compared first, both conditions are true if reset($x) < $y[key($x)]
and reset($y) < $x[key($y)]
.
But consider another implementation, which would solve this problem:
$x < $y <=> cmp($x, $y) == -1
$x > $y <=> cmp($x, $y) == +1
Now <
and >
are consistent when the order of the operands is fixed, but we now get weird behavior when we swap the operands because we could still have cmp($x, $y) == -1
and cmp($y, $x) == -1
, which would mean $x < $y
and $y < $x
would both be true.
In sum, the only solution would be to fix the comparison function so that its behavior was antisymmetric, i.e. so that cmp($x, $y) == - cmp($y, $x)
, at least within a set of elements that are claimed to be comparable.
I may be wrong but I don't think you can compare arrays that way. I always assumed one can check for equality or inequality, but not compare quantities with < and >.
The man page on array operators seems to confirm this.
精彩评论