【问题标题】:PHP Short-Circuit Evaluation (Good/Bad?)PHP 短路评估(好/坏?)
【发布时间】:2012-05-17 10:16:21
【问题描述】:

这是一个一般性的问题,但为了解释它,我将使用一个具体的例子。

我有一个加载文档的函数。如果该文档不存在,它将创建它,如果它确实存在,则将其转换为 JSON 数组。我总是希望这个函数返回某种数组,无论json_decode() 是否存在问题或者文件不存在。目前我正在这样做......

function load($file) {
    if( ! file_exists($file)) {
        $handle = fopen($file, 'w');
        fclose($handle);
    }

    $raw = file_get_contents($file);
    $contents = json_decode($raw, TRUE);

    return( ! $contents ? array() : $contents);
    //cant use ternary shorthand "?:" in PHP 5.2, otherwise this would be shorter
}

现在,上面的代码没有任何问题(至少我认为没有问题,并且工作正常)。然而,我一直在寻找改进我的代码并在保持其清晰易读的同时对其进行压缩的方法。而那个 return 语句一直困扰着我,因为它看起来效率很低。所以今天我开始思考并且发生了一些事情。我记得看到 mysql 教程对connect() or die(); 的效果有所帮助,所以我想,为什么不json_decode() or array();?这甚至会起作用吗?所以我重写了我的函数来找出...

function load($file) {
    if( ! file_exists($file)) {
        $handle = fopen($file, 'w');
        fclose($handle);
    }

    $raw = file_get_contents($file);
    return json_decode($raw, TRUE) or array();
}

似乎如此,甚至读起来也足够令人愉快。继续我的下一轮问题。这是好习惯吗?我明白,但其他人会吗?它真的有效吗,或者这是一个快乐结局的错误?我环顾四周,发现我要问的是所谓的短路评估,而不是错误。很高兴知道。我使用这个新术语来优化我的搜索并想出了更多的材料。

Blog Entry

Wikipedia

在我所询问的方式中,我发现的几乎所有关于使用短路的内容都很少提及 MySQL 连接。现在,我知道大多数人反对使用or die() 术语,但这仅仅是因为它是一种处理错误的不雅方式。这对于我所询问的方法来说不是问题,因为我不想使用or die()。还有其他理由不使用它吗? Wikipedia 似乎是这样认为的,但仅参考 C。我知道 PHP 是用 C 编写的,所以这绝对是相关信息。但是这个问题在 PHP 编译中已经被淘汰了吗?如果不是,它是否像维基百科所说的那样糟糕?

这是来自维基百科的 sn-p。

Wikipedia - “短路会导致现代处理器的分支预测错误,并显着降低性能(一个显着的例子是高度优化的光线,光线追踪中的轴对齐框相交代码)[需要澄清]。一些编译器可以检测到这种情况并发出更快的代码,但由于可能违反 C 标准,这并不总是可能的。高度优化的代码应该使用其他方式来执行此操作(如手动使用汇编代码)"

大家觉得呢?

编辑

我在另一个论坛上进行了民意调查,并在那里获得了一些不错的结果。普遍的共识似乎是这种形式的变量赋值虽然有效,但不是首选,在现实世界中甚至可能被认为是不好的形式。我会继续关注地面,如果有任何新情况出现,我会更新。感谢 Corbin 和 Matt 的意见,特别是 Corbin 澄清了一些事情。如果您有兴趣,Here 是论坛帖子的链接。

【问题讨论】:

  • @DaveRandom 来自反对 php 的人的典型回答
  • 在您最初的退货声明中,为什么要加入否定的内容? return $contents ? $contents : array();。也不需要括号。
  • @Madbreaks:这实际上是来自我删除的先前条件。它最初看起来像 return( ! isset($contents) || ! $contents ? array() : $contents); 这可能更能说明为什么会这样,我只是在更改它时忽略了重新定位其余部分。

标签: php short-circuiting


【解决方案1】:

你问了几个不同的问题,所以我会尽力解决所有问题。

错过的分支预测:除非您使用 C 或汇编进行编码,否则不要担心这一点。在 PHP 中,你离硬件太远了,考虑分支预测对你没有帮助。无论哪种方式,这将是一个非常微小的优化,尤其是在一个开始进行大量字符串解析的函数中。

还有其他理由不使用它吗? Wikipedia 似乎是这样认为的,但仅参考 C。我知道 PHP 是用 C 编写的,所以这绝对是相关信息。

PHP 可能会将其解析为不同的执行结构。除非您计划运行此功能数百万次,或者您知道这是一个瓶颈,否则我不会担心。在 2012 年,我发现使用 or 短路几乎不可能导致十亿分之一秒的差异。

至于格式,我觉得$a or $b 相当难看。我的头脑并不理解它在 if 子句中看到的短路。

if (a() || b())

我非常清楚,只有当 a() 的计算结果不为 true 时,b() 才会执行。

但是:

return a() or b();

对我来说没有同样的清晰度。

这显然只是一种观点,但我将提供两种关于我如何编写它的替代方案(在我看来,这更清楚一点):

function load($file) {
    if (!file_exists($file)) {
        touch($file);
        return array();
    }

    $raw = file_get_contents($file);

    $contents = json_decode($raw, true);

    if (is_array($contents)) {
        return $contents;
    } else {
        return array();
    }

}

如果您不在乎文件是否真的被创建,您可以更进一步:

function load($file) {

    $raw = file_get_contents($file);

    if ($raw !== false) {
        $contents = json_decode($raw, true);
        if ($contents !== null) {
            return $contents;
        }
    }

    return array();

}

我想这些代码 sn-ps 归结为个人喜好。第二个 sn-p 可能是我要使用的那个。其中的关键路径可能会更清晰一些,但我觉得它在不牺牲可理解性的情况下保持简洁。

编辑:如果你是一个函数返回 1 的人,以下可能更可取:

function load($file) {

    $contents = array();

    $raw = file_get_contents($file);

    if ($raw !== false) {
        $contents = json_decode($raw, true);
        if ($contents === null) {
            $contents = array();
        }
    }

    return $contents;

}

【讨论】:

  • @Madbreaks 我期待有人这么说。如果您尽早解决错误情况,我发现会更清楚。为什么要通过函数的其余部分来处理错误?而且,您可以一目了然,立即查看顶部的功能检查内容。 (尽管我提供的替代方案都没有为此提供特别好的案例。)
  • 我实际上同意 Madbreaks。我认为一个出口比许多出口要好。多少人只喜欢一个入口。它的概念相同。不必跟踪多个返回意味着我可以更轻松地控制退出函数的内容以及如何处理它。谢谢您的回答。将其更改为return a() || b() 对您来说更有意义吗?两者都是有效的,我也更喜欢后者,我只是使用or 来保持前面的例子。
  • 我不认为是 or 而不是 || 让我觉得奇怪。我不知道。出于某种原因,使用短路表达式的值对我来说看起来很奇怪。就像我在答案中所说的那样,不过,可能只有我一个人有这种古怪。这归结为个人喜好,但作为一个过去总是使用 1 个 return 语句的人,我相信函数确实可以用多个更干净地编写。
  • @showerhead 另外,使用“入口点”和“出口点”的术语......这实际上是人们有一个返回声明心态的很大一部分。在机器代码中,实际上并没有“函数”,因为您只知道要跳转到的地址。 “一个入口”的思想源于汇编编码时代,当时它认为跳入函数中间是非常糟糕的形式。 (在这个问题的背景下并没有真正影响任何事情,但我只是觉得这很有趣。)
  • 我有很多事情要考虑。这个问题我再炖一会,看看有没有活动,不然明天给你答案。感谢您的详细解答。
【解决方案2】:

将你的代码压缩成你能得到的最简单的行并不总是最好的方法,因为通常压缩代码看起来很酷,但通常很难阅读。如果您对代码和可读性有任何疑问,我建议您在代码中添加一些标准 cmets,以便任何人都可以仅从您的 cmets 中理解代码。

就最佳实践而言,这是一个见仁见智的问题,如果您对它感到满意,那就继续吧,如果需要,您可以随时在项目生命周期后重新访问代码

【讨论】:

  • 是的,我知道,如果我想澄清一点,我通常会添加 cmets。我确实喜欢确保我的代码仍然清晰易读,这就是为什么我并不总是压缩它,只要我认为它很容易理解。
【解决方案3】:

我确实喜欢短路声明,因为它是一种进行单行变量检查的方法。

我更喜欢:

isset($value) or $value = 0;

而不是:

if (!isset($value)) {
  $value = 0;
}

但我没有直接在退货中使用它,这篇文章很想尝试。

遗憾的是,它不能正常工作,至少对我来说:

return $data[$key] or $data[1];

在我期待一个数组时,在所有情况下都会返回值 1。

以下工作顺利:

// Make sure $key is valid.
$data[$key] or $key = 1;

return $data[$key];

但令我惊讶的是,当 $data 中不存在 $key 时,PHP 没有抛出任何错误。

【讨论】:

    猜你喜欢
    • 2017-01-21
    • 2011-08-07
    • 2010-09-10
    • 1970-01-01
    • 2015-11-14
    • 2012-02-10
    • 2010-12-21
    相关资源
    最近更新 更多