PHP有短路求值吗?

86

考虑以下代码:

if (is_valid($string) && up_to_length($string) && file_exists($file)) 
{
    ......
}
如果 is_valid($string) 返回 false,PHP 解释器是否仍会检查后续条件,如 up_to_length($string)
如果是这样,那么为什么它要做额外的工作呢?

一些阅读材料可以与所有这些答案一起使用:http://en.wikipedia.org/wiki/Short-circuit_evaluation - Aaron W.
1
这是关于短路评估的话题,不同的编程语言处理方式也不同。 - AJ.
1
@AJ:说实话,我还没有见过一种(严肃/广泛使用的)编程语言,它不具备逻辑与和逻辑或的短路求值。你有例子吗? - user395760
1
@delnan - 我认为你的意思是“大多数广泛使用的编程语言都没有实现任何急切运算符。” - AJ.
@AJ:在无数种语言中,运算符+是完全急切的,二进制和/或也是如此。不,我确定我在询问逻辑与和逻辑或不短路的语言。 - user395760
4
@delnan - 我想到了VB和Fortran... - AJ.
8个回答

111

是的,PHP解释器是“懒惰”的,这意味着它会尽可能少地进行比较以评估条件。

如果您想验证这一点,请尝试以下代码:

function saySomething()
{
    echo 'hi!';
    return true;
}

if (false && saySomething())
{
    echo 'statement evaluated to true';
}

30
短路求值并不等同于懒惰求值。 - user395760
1
@delnan:这是惰性求值的一个例子。请参见http://books.google.com/books?id=vogP3P2L4tgC&pg=PA367#v=onepage&q=lazy&f=false。 - Zach Rattner
1
一个微小的例子,也是一个有争议的例子(同样地,人们可以把if语句称为惰性求值的例子)。惰性求值更加普遍——它适用于任何表达式任何地方(尽管你通常可以在不使用该值的情况下强制进行求值)。那本书摘录正确地阐述了这一点。 - user395760
1
你知道有什么方法可以禁用这种行为吗? - Radu Murzea
5
@RaduMurzea说,短路并不是我期望被切换的东西,因为它可能对整个程序和导入的库产生负面影响。考虑这样一种情况,你在读取变量之前检查它是否已设置 if (isset($var['index']) && $var['index'] === "something")。如果你需要评估布尔表达式中的每个条件,则在 if 语句之前评估它们并保存结果,然后在 if 条件中检查结果。 - Justin C
显示剩余2条评论

11

位运算符&|。它们总是对两个操作数进行评估。

逻辑运算符ANDOR&&||,这四个运算符只在需要时评估其右侧。

  • ANDOR的优先级低于&&||。请参见下面的示例。

 

摘自PHP手册:

// The result of the expression (false || true) is assigned to $e
// Acts like: ($e = (false || true))
$e = false || true;

// The constant false is assigned to $f before the "or" operation occurs
// Acts like: (($f = false) or true)
$f = false or true;
在这个例子中,e将会是truef将会是false

8
“AND和OR总是同时计算两个操作数”——这是不正确的。它们只比&&||优先级低,除此之外,在语义上与&&||相同。 - axiac
1
将以下与编程有关的内容从英语翻译为中文。仅返回已翻译的文本:将从手册中添加良好的通知:“请注意,PHP的布尔运算符始终返回布尔值...而不是返回最后评估表达式的其他语言的值。” - Hebe

11

是的,它可以。这里有一个小技巧,依赖于短路求值。有时候你可能会有一个小的if语句,你更希望将其写成三元运算符,例如:

    if ($confirmed) {
        $answer = 'Yes';
    } else {
        $answer = 'No';
    }

可以重写为:

   $answer = $confirmed ? 'Yes' : 'No';

但是如果“是”分支也需要运行某些功能怎么办?

    if ($confirmed) {
        do_something();

        $answer = 'Yes';
    } else {
        $answer = 'No';
    }

好的,将其改写成三目运算符仍然是可能的,因为存在短路求值:

    $answer = $confirmed && (do_something() || true) ? 'Yes' : 'No';

在这种情况下,表达式(do_something() || true)并没有改变三元操作的总体结果,但确保了三元条件始终为true,忽略了do_something()的返回值。

6

根据我的研究,PHP似乎没有像JavaScript一样的&&短路运算符。

我运行了以下测试:

$one = true;

$two = 'Cabbage';

$test = $one && $two;

echo $test;

PHP 7.0.8返回了1,而不是Cabbage


4
我认为 && 会使PHP将所有内容强制转换为布尔值,这样 $two 就会被转换为 true,随后输出为 1。要测试这个条件,你需要使用 true||(echo 'no short circuit') 或类似的语句。 - Teepeemm

3
不,如果第一个条件不满足,它就不再检查其他条件。

2

我已经创建了自己的短路评估逻辑,不幸的是它与JavaScript的快速语法完全不同,但也许这是一个你可能会发现有用的解决方案:

$short_circuit_isset = function($var, $default_value = NULL) {
    return  (isset($var)) ? : $default_value;
};

$return_title = $short_circuit_isset( $_GET['returntitle'], 'God');

// Should return type 'String' value 'God', if get param is not set

我不记得以下逻辑来自哪里,但是如果你按照以下步骤进行:

(isset($var)) ? : $default_value;

您可以在问号后面跳过再次编写真实条件变量,例如:
(isset($super_long_var_name)) ? $super_long_var_name : $default_value;

非常重要的观察,当使用三元运算符时,您会注意到如果进行比较,它只会传递该比较的值,因为不只有一个变量。例如:

$num = 1;
$num2 = 2;
var_dump( ($num < $num2) ? : 'oh snap' );
// outputs bool 'true'

2
那个带有 ?: 的逻辑与 JavaScript/Perl 的 || 一样工作。 - trysis
不完全是try语句——左侧参数似乎被扫描以查找表达式,如果为真,则返回该表达式——无论其周围是否有函数。(例如:isset($var) ?: '1' 如果第一个表达式为真,则产生$var)。 - Gerard ONeill

-6

我的选择:在 PHP 中不要相信短路评估...

function saySomething()
{
    print ('hi!');
    return true;
}

if (1 || saySomething())
{
    print('statement evaluated to true');
}

条件语句中的第二部分1 || saySomething()是无关紧要的,因为它总是返回true。不幸的是,saySomething()被评估和执行了。

也许我误解了短路表达式的确切逻辑,但这对我来说看起来并不像"它将尽可能少地进行比较"

此外,这不仅是性能问题,如果您在比较中进行赋值或者做一些与仅仅比较不同的事情,您可能会得到不同的结果。

无论如何...要小心。


这可能是短路评估或渴望的原因。只要它知道结果将为假,它就会停止。 - grantwparks
9
我刚刚运行了你的代码,但是saySomething()没有被执行。PHP版本为5.3.13。 - grantwparks
3
+1 授予权限给 grantwparks,saySomething() 函数 不会 被执行。PHP 会正确地进行短路运算。我已经在 PHP 中写了这种代码将近十年了,我不记得曾经卡在这个问题上过。偶尔我会写更冗长的代码,如果我觉得额外的清晰度很重要,但通常一个代码注释就足以解决这个问题。 - Jason
5
你是正确的。我再次运行了自己的测试,发现函数saySomething()从未被执行。我不知道在我的第一次测试中出了什么问题。抱歉:( - Beto Aveiga
1
这是证明 PHP 行为一致的一个例子。如果 OR 条件中的第一个值为真,那么很明显如果它为真,则不会处理其余语句,这与短路行为一致。 - Faraz
重新表述第二个打印语句可以更清晰地揭示答案...function saySomething() { print ('嗨!'); return true; } if (1 || saySomething()) { print('再见'); } - chillywilly

-7

小提示:如果你想避免懒惰检查并运行条件的每个部分,那么你需要像这样使用逻辑与:

if (condition1 & condition2) {
 echo "both true";
}
else {
 echo "one or both false";
}

当你需要调用两个函数时,即使第一个函数返回false,这将是很有用的。


3
这不是“逻辑AND”,而是一种位运算符。它可能会让您感到惊讶,因为它的工作方式并不完全像逻辑 && - deceze
1
这是逻辑 AND 门,为什么你说它不是呢?就像我说的那样,并不是 && 或者 AND。 这并不会让我惊讶,因为我知道它是如何工作的。 就像检查这张表格并使用正确的返回类型一样简单:1 & 1 = 1 1 & 0 = 0 0 & 1 = 0 0 & 0 = 0 - Patricio Rossi
2
在PHP中,逻辑AND按位AND是有区别的;这里使用的不是逻辑AND。即使知道它的规则,未来的读者可能不知道。 - deceze
2
这个答案是完全错误的。如果 $a == 1 并且 $b == 2,那么 $a$b 都不是 FALSE,但是 $a & $b == FALSE - axiac
1
还是有误,请认真阅读一下我的意思:如果你需要检查两个「条件」的结果。你好像认为我在比较变量,但实际上我并没有。 在你的例子中,你使用了两个变量(进行位运算),这不是我的意思。我使用的是两个「条件」,可能是函数调用的结果,它们是 BOOLEAN 类型。 - Patricio Rossi
显示剩余11条评论

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