PHP数组比较算法

17

在尝试模拟一些 PHP 行为时,我偶然发现了这个问题:

    $a=array(0 => 1, 'test' => 2);
    $b=array('test' => 3, 0 => 1);
    var_dump($a==$b, $a>$b, $b>$a);
根据var_dump的输出,$b$a大。在PHP手册中有一个标准数组比较的转录,其中规定数组的值会逐个进行比较,如果第一个数组中的键在第二个数组中不存在,则这些数组是无法比较的。到目前为止还好。但是如果我尝试这样做(只更改$a的第一个元素):
    $a=array(0 => 2, 'test' => 2);
    $b=array('test' => 3, 0 => 1);
    var_dump($a==$b, $a>$b, $b>$a);
三个比较结果都是false。对我来说,这似乎是"不可比较的"(因为>结果与<结果相同,但数组既不==也不一样,这毫无意义),但这与PHP手册的记录不符。两个数组中都存在相同的键名,我期望这次$a更大,因为在$a中键名为0的值更大(分别是2和1)。

我已经尝试查看了PHP源代码,并在zend_hash.c中找到了zend_hash_compare(),但那里的代码似乎与手册描述的一样。

这里到底发生了什么?


1
有趣的是,如果您反转声明元素的顺序,问题就会消失... - DaveRandom
首先,您正在尝试让 PHP 将普通整数数组转换为映射数组。您应该选择其中一个。我从未必须将映射数组与常规数组混合使用。尝试在定义数组时更改值,告诉 PHP 首先将两者都映射。 - Adam Fowler
我猜zend_hash_compare里面有个bug,但是我找不到它。如果你使用手册中的示例比较函数测试你的数组,那么你会得到预期的结果:$a=array(0 => 2, 'test' => 2); $b=array('test' => 3, 0 => 1); $r = standard_array_compare($a, $b); // c/p from manual var_dump( $r );结果:int(1) 这应该在https://bugs.php.net/上报告。 - DamirR
我考虑过这个,但通常当我怀疑 PHP 中存在错误时,它只是存在于我的头脑中;-) 如果这没有发现问题,我会尝试那个。 - Wolfgang Stengel
我并不打算在生产代码中使用它,也不是为了解决某个问题,我只是好奇为什么会发生这种情况。这是有效的语法和记录行为,但是它并不合理。 - Wolfgang Stengel
3个回答

6
编辑:正如Joachim所示,它涉及到所谓的顺序。引用他的话: "$a>$b循环遍历b并首先找到'test'。 'test'在$b中更大,因此$b更大,并返回false。$b>$a循环遍历a并首先找到'0'。'0'在$a中更大,因此$a更大,并返回false。"
-- 原始帖子 --
我不确定我的理解是否正确;我之前没有见过这种情况,只是简单地研究了一下(顺便说一句,你的问题非常好!)。无论如何,似乎PHP文档有误,或者这是一个bug(如果是这样,您可能需要提交它),原因如下:
在zend_hash.c的zend_hash_compare()中,似乎对ordered有些困惑(我正在查看第1514行和第1552-1561行,我最好的猜测是这是问题所在,而不需要进行大量测试)。
这就是我的意思; 试试这个:
$a=array(0 => 2, 'test' => 2);
$b=array(0 => 1, 'test' => 3);
var_dump($a==$b, $a>$b, $b>$a);

请注意,我仅仅交换了索引的顺序,而$a>$b返回true。此外,请参阅以下内容:

$x=array(0 => 2, 'test' => 2);
$y = $x;
$y[0] = 1; $y['test'] = 3;
var_dump($x==$y, $x>$y, $y>$x);

请注意,$x>$y 也返回 true。换句话说,PHP 不仅匹配数组键!它还关心这些键在数组中的顺序!您可以通过创建一个“基本”数组并将其“复制”到新变量(在我的 x/y 示例中)之前进行修改来防止出现此情况,或者如果您愿意,可以创建一个对象。
换句话说,PHP 看起来不仅查看键值,而且还查看键值和键顺序。
我再次强调,我不知道这是否是预期行为(如果是,他们应该在 PHP 手册中注明),还是一个错误/故障等(我认为更有可能是后者)。但无论哪种方式,我发现它首先按键数进行比较(在 zend_hash.c 的第 1496-1501 行),然后按键值和键顺序进行比较。

这很可能不是一个错误,只是由于遍历的数组不同导致的结果。请参阅我的答案以获取详细信息。 - Joachim Isaksson
也许是这样,但是查看文档,特别是看一下“示例#1”,它在一个逻辑示例中暗示顺序重要。如果这是真的——如果意图是尽管键的顺序不同仍然有结果,但键的顺序确实是一个因素——那么我个人会将其归类为错误。 - cegfault
@cegfault 感谢您详细的想法。我已经反复查看了zend_hash_compare(),但是没有发现问题所在。也许Joachim说的有道理,因为顺序似乎会产生影响。 - Wolfgang Stengel
@cegfault,我不太确定你的意思,副本(示例#1)使用foreach,它确实考虑了$op1的数组顺序。我看到的是同一个例子吗? :) - Joachim Isaksson
1
我同意@JoachimIsaksson的观点。我没有时间查看zend_hash_compare从哪里调用。如果PHP确实在处理传递到zend_hash_compare中的数组的顺序,那么他很可能是正确的。我建议至少在PHP上发表评论,因为这可能至少应该是已知的行为,如果不被归类为错误。 - cegfault
显示剩余4条评论

5
似乎在比较循环中,>的情况是针对右侧数组进行的,而<的情况是针对左侧数组进行的,即始终针对所谓的“较小”数组。元素的顺序很重要,因为转录代码中的foreach循环遵循数组顺序。
换句话说: $a>$b循环遍历b并首先找到“test”。在$b中,“test”更大,因此$b更大且返回false。 $b>$a循环遍历a并首先找到“0”。在$a中,“0”更大,因此$a更大且返回false。
这实际上是有意义的,"较大"数组可以包含"较小"数组没有的元素,只要所有共同的元素都更大。

在尝试验证时,我发现虽然zend_operators.c中有is_smaller_function()is_smaller_or_equal_function()函数,但是没有相反的函数,如is_bigger_function()is_larger_function()。也许PHP确实是在测试更大的值时翻转参数并调用较小的函数,但我也找不到证据支持这一点。 - Wolfgang Stengel
2
找到了。实际上,PHP在解析阶段就已经翻转了参数。令牌T_IS_GREATER_OR_EQUAL会被转换为zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL...),参数被反转。谢谢! - Wolfgang Stengel
@WolfgangStengel 谢谢你自己,很高兴得到确认 :) - Joachim Isaksson

-1

我认为这里是逐一比较的,所以$a[0]>$b[0]$a['test']<$b['test']。 你不能说哪个数组更大。


1
请确保在回答问题之前,您至少对所讨论的内容有一定的了解。查看问题中发布的链接可能会有所帮助... - DaveRandom
@DaveRandom 在回答之前,我已经阅读了链接。文中写道:成员较少的数组较小,如果操作数1中的键未在操作数2中找到,则数组不可比较,否则-逐个按值比较(请参见以下示例),但是示例仅比较一个值-我认为示例不准确。就这些。 - Tomas
手册中的转录也只是比较一个值,如果第一个值已经不同了。 - Wolfgang Stengel
@Wolfgang 是的,你说得对,但也许我不理解英语,但对我来说,“value by value”听起来像是比较所有值,而这个例子只比较了一个。 - Tomas
我必须承认我错了。这个算法将数组像单词一样进行比较,所以我的答案完全错误。 - Tomas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接