确保给定列表中只有一个布尔值为真?

8
如果我有以下布尔值
const YESTERDAY = false;
const TODAY = true;
const TOMORROW = false;

我应该写什么代码才能确保只有一个条件为真?

我已经尝试了以下代码:

$x = self::YESTERDAY ^ self::TODAY ^ self::TOMORROW;

问题在于将三个常量都设置为true时,$x也是true

你可以使用OR条件将所有组合放在一起,如下:(YESTERDAY=true AND TODAY=false AND TOMORROW=false) or (YESTERDAY=false AND TODAY=true AND TOMORROW=false) or (YESTERDAY=false AND TODAY=false AND TOMORROW=true) - Naveen Babu
他需要确保true确切地等于1,而不是至少一个。 - devdRew
6个回答

9
,然后如果$x === 1;,你就得到了你需要的结果。 编辑: 即使没有类型转换(int),由于@DaveRandom的帮助,它也可以正常工作,所以对我来说:if (self::YESTERDAY + self::TODAY + self::TOMORROW == 1) {}

我考虑过这个问题,但最好避免所有额外的处理,有没有一种位运算的方式来做到这一点呢? - Matt
测试后发现,甚至不需要(int)强制转换,它会隐式地发生。请点赞确认。 - DaveRandom
1
这比其他任何解决方案都要整洁得多。@Matt 几乎正确地使用了 XOR (^) 运算符,但不幸的是 true ^ true ^ true 将被计算为 true,因为 PHP 将其解释为 (true ^ true) ^ true,即 false ^ true - WildlyInaccurate
这是一个惊人的解决方案。谢谢 :D - Howard Lie

3

我能想到的最简洁的方法是使用array_sum()

if (array_sum(array(self::YESTERDAY, self::TODAY, self::TOMORROW)) == 1) {
  // Do something
}

编辑 实际上,你只需要在你原本的尝试中用 + 替换 ^,就可以达到相同的效果:

$x = self::YESTERDAY + self::TODAY + self::TOMORROW;

此代码将$x转换为TRUE值的数量。因此,对于布尔输出,请使用:

$ok = self::YESTERDAY + self::TODAY + self::TOMORROW === 1;

实际上,对devdRew答案的变化是最简洁的 - 如果您只是在原始代码中用+替换^,它会做同样的事情 - 您不需要(int)转换,因为这会隐式发生。 - DaveRandom

0

这只是作为devdRew答案的另一种选择

$x = array_count_values(array((int) self::YESTERDAY,(int) self::TODAY,(int) self::TOMORROW));
if (isset($x[1]) && $x[1] == 1) {
    echo 'Only one TRUE';
}

0
你可以遍历一个变量列表,并在找到第二个为真的布尔值时中断:
$moreThanOneTrue=false;
$oneTrue;
foreach ($BOOL_VAR_ARRAY as $bool) {
    if ($bool) {
        if($oneTrue) {
            $moreThanOneTrue=true;
            break;
        }
        $oneTrue=true;
    }
}

如果您有三个以上的变量,这样更方便。


如果有超过3个,我认为array_sum方法比这个更简单。 - Matt

0

如果且仅当其中一个为真而其他为假时,x将返回true。

$x = ($a && !($b || $c)) || ($b && !($a || $c)) || ($c && !($a || $b));

这段代码可能不太好,但是能够运行。


0
"

a ^ b ^ c ^ (a & b & c)是您要查找的表达式。

"

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