PHP在案例声明中使用switch()和逻辑或(XOR)的奇怪行为

时间:2013-07-19 19:38:34

标签: php php-5.3 bitwise-operators logical-operators

注意:我知道这是不可接受的代码,我想了解解释器正在做什么,而不是建议如何实现相同的结果!

我已经阅读了足够的内容,意识到我不能也不应该尝试使用|,||或者XOR定义一个开关案例 - 所以请不要评论“不要那样做”的效果,我只是试图理解解释器做什么这些语句和了解行为的陌生感。我正在使用PHP 5.3.1。

这是我实际打算做的,现在正在使用,请不要推荐代码:

for ($i=0; $i<count($this->header); $i++) {
            switch($i) {
                case 0:
                    $this->header[ $i ] = $this->header[ $i ] ? $this->header[ $i ] : -5;   //angle from goal
                    break;
                case 1:
                    $this->header[$i] = $this->header[$i] ? $this->header[$i] : -5;         //miss penalty
                    break;
                case 2:
                    $this->header[$i] = $this->header[$i] ? $this->header[$i] : -10;         //miss penalty
                    break;
                case 3:
                    $this->header[ $i ] = $this->header[ $i ] ? $this->header[ $i ] : -10;  //error penalty
                    break;
            }
        }

但如果我能做的话,我很好奇:

for ($i=0; $i<count($this->header); $i++) {
            switch($i) {
                case 0 || 1:
                    $this->header[ $i ] = $this->header[ $i ] ? $this->header[ $i ] : 15;   //angle from goal
                    break;
                case 2 || 3:
                    $this->header[$i] = $this->header[$i] ? $this->header[$i] : -5;         //miss penalty
                    break;
            }
        }

而且,反过来说,这确实会运行,但是非常慢慢地(比如,秒),虽然它当然不是按照我想要的方式评估(0||1)(事实上)今天是关于按位和逻辑运算符之间差异的一课。)

但对我来说更好奇的是我可以做到这一点,如果非常缓慢而不是我想要的结果:

for ($i=0; $i<count($this->header); $i++) {
            switch($i) {
                case 0 XOR 1:
                    $this->header[ $i ] = $this->header[ $i ] ? $this->header[ $i ] : 15;   //angle from goal
                    break;
                case 2:
                    $this->header[$i] = $this->header[$i] ? $this->header[$i] : -5;         //miss penalty
                    break;
                case 3:
                    $this->header[$i] = $this->header[$i] ? $this->header[$i] : -5;         //miss penalty
                    break;
            }
        }

但我可以这样做:

for ($i=0; $i<count($this->header); $i++) {
            switch($i) {
                case 0 XOR 1:
                    $this->header[ $i ] = $this->header[ $i ] ? $this->header[ $i ] : 15;   //angle from goal
                    break;
                case 2 XOR 3:
                    $this->header[$i] = $this->header[$i] ? $this->header[$i] : -5;         //miss penalty
                    break;
            }
        }

这是PHP甚至不会评估的这些可怕,可怕的想法中唯一的一个。

我的问题是:为什么评估这些语句会花费更长时间,为什么最后一个例子不会运行?我认为PHP正在解释0 XOR 1并比较truefalse,但我无法替换它们并让它仍然评估。有谁知道这里发生了什么?谢谢!


更新

评论请求var_dump($this->header)(如果不明显,我将我的初始switch语句从7个案例截断为4个,以避免发送相同代码的垃圾邮件,但是,鉴于var_dump()被要求,我选择发布整件事,万一它揭示了我无法预见的事情!)。另外,是的,事实证明,由于在设置array_values()之前忘记调用$this->header,我一直在使用关联数组,在下面的第二个答案中,已经将此错误变为解释持续时间switch声明,而第一个答案和第二个答案是逻辑的伟大演练。

array(12) {
  ["theZone"]=>
  NULL
  ["leftMiss"]=>
  NULL
  ["rightMiss"]=>
  NULL
  ["leftError"]=>
  NULL
  ["rightError"]=>
  NULL
  ["leftHit"]=>
  NULL
  ["rightHit"]=>
  NULL
  ["accuracy"]=>
  string(5) "false"
  ["rt"]=>
  string(4) "true"
  ["disease"]=>
  string(3) "yes"
  ["bars"]=>
  string(3) "yes"
  ["endMessage"]=>
  NULL
}

2 个答案:

答案 0 :(得分:2)

case值必须为SINGLE值。你不能2 || 3,因为它会评估为

case (2 or 3) -> case TRUE

同样,如果你使用&&and),你就会得到

case (0 and 1) -> case FALSE

您可以对OR逻辑使用'fallthrough'行为:

case 2:
case 3:
   ...code here ...
   break;
XOR,你不能这样做。不是简单的案例陈述。

评论后续:

for ($i = 0; $i < 5; $i++) {
   switch($i) {
       case 0 xor 1:  echo "0 xor 1: $i\n"; break;
       case 2 xor 3:  echo "2 xor 3: $i\n"; break;
   }
}

将输出

2x3: 0
0x1: 1
0x1: 2
0x1: 3
0x1: 4

php > var_dump(2 xor 3);
bool(false)
php > var_dump(0 xor 1);
bool(true)

请记住,切换案例会评估相同的==。他们是懒惰的比较,而不是检查类型。因此,2 xor 3的评估方式与case false的情况相同,这使得$ i = 0条件匹配。

答案 1 :(得分:1)

哈哈,你在那里做的很有趣。

这不是让你陷入性能问题的开关。它可能是你的循环与你的开关结合。

我尝试用给出的第二个例子解释它。

但是,我只需要猜测,所以我必须假设您的$ this-&gt;标头数组如下所示(这将是导致性能泄漏的唯一原因):

$this->header = array(
  999 => 10,   // the value is irrelevant, only the key (2) matters
  998 => 15
);

现在让我们玩解释器调试器。

set $i to 0
is $i<count($this->header)? yes, because header size is 2
is ($i == (0 || 1))? no, because (0 == (0 || 1)) equals (0 == true) equals (false == true) can never be true
is ($i == (2 || 3))? no, because (0 == (2 || 3)) equals (0 == true) equals (false == true) will never comply
next loop, increment $i (set $i to 1)
is ($i<count($this->header)? yes, because header size is 2
is ($i == (0 || 1))? yes, because (1 == (0 || 1)) equals (1 == true) equals (true == true)
go into case statement
set $this->header[1] to -5  (remember the index 1 does not exist so far in your array)
next loop, increment $i (set $i to 2)
is $i<count($this->header)? yes, because header size is 3 (remember? you just added a new index which incremented your header size)
is ($i == (0 || 1))? yes, because (2 == (0 || 1)) equals (2 == true equals (true == true)
go into case statement
set $this->header[2] to -5 (again index 2 did not exist so far)
next loop, increment $i (set $i to 3)
is $i<count($this->header)? yes, because header size is 4 (again, you just added a new index)

现在这将继续,直到标题大小等于$ i。在上面的例子中,这将在998次迭代之后发生,因为这将是第一次没有新的索引创建(现有的一个 - 998 - 只会被使用),导致计数($ this-&gt;标题)不改变。

在你的第一个例子中,这不会发生。在第四次迭代之后,标头大小将停止更改,因为case语句尝试比较整数值而不是布尔值。一旦到达索引5,它就会尝试与值1,2,3和4进行比较,并且不会匹配单个值,从而导致没有新的索引创建。

要解决性能问题,您应该确保数组中的每个键都存在,并且还要更改for循环,如下所示:

$size = count($this->header);
for($i=0; $i<$size); $i++) {...}

当然,这不会解决您的逻辑问题。但是,既然你问过发生了什么,我想这应该是一个很好的解释。

var_dump() - 循环前后你的$ this-&gt;标题数组应该证明我是正确的:)

===

很抱歉删除并取消删除此答案。只是想确保它真的像解释的那样。如果你能为我提供你的标题数组的var_dump,那就太好了。