嵌套的 PHP 三元运算符问题:三元运算符输出结果不等于 if - else。

5

我擅长使用PHP三元运算符。然而,我遇到了一个难题,就是无法弄清楚下面的代码为什么不能与if-else等价结构匹配。测试在不同的数字上运行了三次。每个结构的输出都在代码下面。

三元运算符:

$decimal_places = ($max <= 1) ? 2 : ($max > 3) ? 0 : 1;

三元输出:

最大值:-100000 十进制:0

最大值:0.48 十进制:0

最大值:0.15 十进制:0

如果-否则

if($max <= 1)
 $decimal_places = 2;
elseif($max > 3)
 $decimal_places = 0;
else
 $decimal_places = 1;

If-Else输出:

最大值:-100000 十进制:2

最大值:0.48 十进制:2

最大值:0.15 十进制:2

请问有人能告诉我为什么这两个控制结构没有输出相同的数据吗?


4
为什么嵌套三元运算符不是一个好主意: - Mark Baker
1
它们并不是完全等价的,你知道的。整个 elseif 在三元结构中会短路。它们也不应该被嵌套。 - Rafe Kettler
1
从PHP手册上的三元运算符部分(http://de3.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary)中可以知道:建议避免“堆叠”三元表达式。在单个语句中使用多个三元运算符时,PHP的行为是不明显的。 - Gordon
@Mark Baker 我只尝试在非常简单的语句中嵌套它们,因为我见过那些让你想做出暴力行为的三元语句。 - Patrick
所有使用三元运算符的代码都让我想采用暴力手段。没有任何好的理由去使用它们。人们读代码的次数远多于编写代码的次数。使用像三元运算符这样可怕的结构使代码“简洁”是会增加错误并降低可维护性的,正如你在你的问题中所看到的。 - taiganaut
4个回答

18

你的右侧三元表达式需要用括号包裹起来,这样它就会被自己评估为单个表达式:

$decimal_places = ($max <= 1) ? 2 : (($max > 3) ? 0 : 1);

// Another way of looking at it
$decimal_places = ($max <= 1)
                ? 2
                : (($max > 3) ? 0 : 1);
否则,您的三元表达式会从左到右进行求值,导致:
$decimal_places = (($max <= 1) ? 2 : ($max > 3)) ? 0 : 1;

// Another way of looking at it
$decimal_places = (($max <= 1) ? 2 : ($max > 3))
                ? 0
                : 1;

翻译成if-else语句的话,变成了这个样子:

if ($max <= 1)
    $cond = 2;
else
    $cond = ($max > 3);

if ($cond)
    $decimal_places = 0;
else
    $decimal_places = 1;
因此,对于所有$max的值,除了2以外,$decimal_places最终都变成了0,在2的情况下,它会被计算为1

2
代码的执行方式如下:
$decimal_places = (($max <= 1) ? 2 : ($max > 3)) ? 0 : 1;

当且仅当 1 < $max <=3 时,您永远不会得到2和1。这是因为条件运算符是左结合的。解决方案:添加括号以确保您想要的顺序被编码:

$decimal_places = ($max <= 1) ? 2 : (($max > 3) ? 0 : 1);

1

只需加上括号,就像这样:

 $decimal_places = ($max <= 1) ? 2 : (($max > 3) ? 0 : 1);

1

正如其他人所指出的,使用括号。
但是,如果您真的想使其易读,那么怎么样:

$decimal_places =
  ($max <= 1) ? 2 : (
  ($max > 3)  ? 0 : (
  1
));

这看起来仍然非常尴尬,但这种尴尬有一个规则的形状,所以更容易应对。

$drink = 'wine';
return
  ($drink === 'wine')   ? 'vinyard' : (
  ($drink === 'beer')   ? 'brewery' : (
  ($drink === 'juice')  ? 'apple tree' : (
  ($drink === 'coffee') ? 'coffeebeans' : (
  'other'
))));

当然,您可以省略最后一对括号,但这会使它看起来不太规则。

确实非常尴尬,但我还是有一些优点(因为我有很多控件、if语句和switch语句等)。 - FreshPro

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