什么是构建条件逻辑的最佳方法?

4
我可以帮助您翻译这段内容。这是一段关于编程的内容,讨论了条件逻辑的不同结构方式。作者认为只要在脚本中设置错误终止程序(或者您可以想象同样的例子在函数中使用返回),那么以下示例是相等的: 示例1
if($condition1) {
    trigger_error("The script is now terminated");
    }

if($condition2) {
    trigger_error("The script is now terminated");
    }

echo "If either condition was true, we won't see this printed";

Example 2

if(!$condition1) {
    if(!$condition2) {
        echo "If either condition was true, we won't see this printed";
        }
    else {
        trigger_error("The script is now terminated");
        }
    }
else {
    trigger_error("The script is now terminated");
    }

例子 3

if($condition1) {
    trigger_error("The script is now terminated");
    }
else {
    if($condition2) {
        trigger_error("The script is now terminated");
        }
    else {
        echo "If either condition was true, we won't see this printed";
        }
    }

示例4 -- 改编自弗雷泽的回答

function test($condition) { 
    if($condition) {
        trigger_error("The script is now terminated");
        }   
    }

test($condition1);

test($condition2);

echo "If either condition was true, we won't see this printed";

个人而言,我更倾向于像示例1中编写代码。这是因为我觉得通过检查以这种方式结束脚本(或函数)的条件,我可以清楚地定义脚本已执行和未执行的内容,即在该条件之前所有内容都已执行,在该行之后的所有内容都未执行。这意味着当我在第147行遇到错误时,我立即知道发生了什么,从而帮助我更快地找到错误。此外,如果我突然意识到需要先测试$condition2再测试$condition1,我可以通过简单的复制粘贴进行更改。
我看到有很多代码像示例2那样编写,但对我来说,这似乎更难以调试。这是因为,当嵌套变得太深时,错误会在底部的某个遥远的行触发,并且与导致错误的条件被大量嵌套的代码块分隔开。此外,更改条件序列可能会更加混乱。
您可以将两种样式混合在一起,例如示例3,但这似乎会过于复杂化问题,因为所有的“else”实际上都是多余的。
我有什么遗漏吗?如何结构化我的条件代码才是最佳方法?除这些示例之外,是否有更好的方式?是否有特定情况下一种样式可能优于另一种? 编辑:示例4看起来非常有趣,这是我没有考虑过的。您还可以将错误消息作为第二个参数传递。
谢谢!
P.S. 请记住,我可能需要在检查$condition1和$condition2之间执行一些任意步骤,因此任何替代方案都必须考虑到这一点。否则,存在一些微不足道的更好的选择,例如if($condition1 || $condition2)。

2
做爱,不要吵架。 - user1228
“如何最佳地构建我的条件代码结构?”正如我之前所说,如果没有具体的代码,就无法对其进行结构化。 - Your Common Sense
10个回答

8

我属于示例1派系。通常来说,缩进越少越好。

// Exit early if there are errors.
if ($n < 0) {
    die "bad n: $n";
}

// Handle trivial cases without fuss.
if ($n == 0) {
    return 0;
}

/* Now the meat of the function. */
$widget->frob($n);
foreach ($widget->blaxes as $blax) {
    checkFrobbingStatus($blax);
}
// ...And another 20 lines of code.

当你使用if/else并将成功和错误代码放在平行部分时,会使两个代码块看起来相等。实际上,边缘情况和错误条件应该被弱化。通过有意识地尽早处理错误,然后将“重要”代码放在else子句中,我觉得这使得重要代码的可视性更清晰。
“这是所有的前提条件。现在是精华内容。”

说得好 - 你提出了一个很好的观点。相对于集成错误捕获(即做出错误捕获和前置条件不同的假设),你认为这个观点如何适用? - AJ.
1
个人而言,return ($n == 0) ? 0 : frobTheWidget($n); 是更好的方式。 - RobertPitt

3

个人而言,我不喜欢嵌套的if-else语句,所以你提供的例子中#1是我的首选。另一个我会考虑的选项是以下内容。

function test($condition) { 
  if($condition) {
    trigger_error("The script is now terminated");
  }   
}

test($condition1);

//do stuff...

test($condition2);

//passed the tests

编辑:我越想越觉得函数式方法是迄今为止最好的方式,因为它避免了多次编写测试条件逻辑。它还可以提高可读性,因为很明显你正在“测试”条件(只要给函数起一个有意义的名称)。此外,正如问题编辑中指出的那样,向函数传递其他参数也非常容易。

function test($c, $msg) { 
  if($c) {
    trigger_error($msg);
  }   
}

test($condition1, "condition1 error");
test($condition2, "condition2 error");

让我们重新开始,但这次不带刀。 - user1228

2

#1是最清晰易懂的。但是,如果之前结束执行的东西被更改为做其他事情,那么它就会出问题。

仍然可能最好选择#1,但确保用于“停止”的东西明确命名以指示它确实可以停止事物,这样在10年后维护您的代码的人不会因更改它而意外破坏事物。


1

我更喜欢这种风格,即使其中一个条件块被更改以便不退出执行,也不会中断程序。

if($condition1) {
    trigger_error("The script is now terminated");
}
if($condition2) {
    trigger_error("The script is now terminated");
}

if (!$condition1 && !$condition2) {
  echo "If either condition was true, we won't see this printed"; 
}

编辑:错过了 PS,因此更新代码以匹配完整的问题细节。


真的,但在检查$condition1和$condition2之间仍然无法执行代码(请参见我的P.S.)。你将被迫将第二个else if分开成一个else { do stuff here if($condition2) { } },这本质上是示例3。示例3更复杂,但我喜欢你的想法,即代码可能会更改以使终止部分变为非终止部分。不确定是否值得为增加的复杂性付出代价。我想这取决于条件块有多复杂? - Rupert Madden-Abbott
抱歉,我错过了PS。我会选择示例#1,但要使用 if(!$ condition1和!$ condition2) 来保护最后一个echo以提高代码可读性,并保护预期的逻辑免受未来修订的影响。 - Manfre

1

我认为你的方法(示例1)在这种情况下是最有效和最有效的。然而,有时您不希望任何条件停止执行,并且只想在condition1为false时执行condition2。在这些情况下,elseelseif很有效。


1
我建议在可能出现错误的任何部分使用“try”子句,并在每次出现错误时使用“throw“error description””(例如#1)。
这样,您可以在程序中仅有一次错误报告代码(在“catch”子句中),并且将代码拆分为函数不会是重写错误处理的麻烦。

相比使用自定义错误处理程序,这种方法有哪些好处? - Rupert Madden-Abbott

1

我基本上同意Amber的观点,因为你的第一个选项似乎最易读。这是我自己一直在努力解决的问题-到目前为止,我遇到的唯一理由如下:

  • 当通过线性脚本阅读时,第一种形式最清晰,因此非常适合简单的脚本
  • 当您需要确保整洁/清理操作时,第二种形式最干净

我提到第二个原因是因为这是一个棘手的问题。每个脚本可能是更大系统的一部分,实际上,您将注入“退出”代码的脚本元素可能会被多个地方调用。加入一些面向对象编程,您就有了一个真正的潜在麻烦。

我能推荐的最佳经验法则是:如果您的脚本简单且线性,或者您正在进行快速原型设计,则应使用第一种形式,并在该点处停止执行。任何更复杂或“企业级”的东西都将受益于(至少)模块化重新设计,以便您可以隔离方法和调用堆栈-并可能封装面向对象编程构建。

随着现在可用的一些更强大的调试和跟踪工具,这更多地成为个人风格而不是必要性的问题。您可能考虑的另一个选项是在每个退出区域之前(以及可能之后)的注释中放置信息,以清楚地说明应满足(或失败)的标准的替代方案。

编辑:

我认为Fraser's answer对于封装是最干净的。唯一需要补充的是,您可能会从将对象或哈希数组传递到标准的“退出,我已经死了”方法中受益,以便您可以修改提供给函数的信息,而无需一直更改参数列表(非常烦人...)。

话虽如此-在可能需要清理中间状态的生产系统中要小心。


0

示例 5

if($condition1 || $condition2)
{
    echo "If either condition was true, we won't see this printed";
}else
{
    trigger_error("The script is now terminated");
}

示例 6

function allAreTrue()
{
    foreach(func_get_args() as $check)
    {
       if(!$check)
       {
           return false;
       }
    }
    return true;
}

if(allAreTrue(true,true,$condition1,$condition2,false))
{
   exit("Invalid Arguments");
}

//Continue 

两个示例都忽略了“我可能需要在检查$condition1和$condition2之间执行一些任意步骤,因此任何替代方案都必须考虑到这一点...” 此外,示例6“allAreTrue”始终返回false... 应该是:function allAreTrue() { foreach(func_get_args() as $check) { if(!$check) { return false; } } return true; } - Fraser
6和Fraser的方法一样愚蠢。你不能像函数参数一样发送条件。这些布尔值有什么用?真正的条件可以是SQL查询结果、函数调用、逻辑运算符或任何东西。但只有布尔值?你从哪里得到它?另一个条件在别处吗? - Your Common Sense
虽然5是可以接受的,也是这里少数几个合理的例子之一。 - Your Common Sense
@ Shrapnel上校 - 示例5没有回答问题。用非常简单的术语来说,布尔条件可以是任何操作的结果。如果你不理解这一点,那么你就不理解编程最基本的概念。 - Fraser
@Col - p.s. 当你在这里使用完全相同的逻辑回答时,你似乎理解得足够好:https://dev59.com/fXE85IYBdhLWcg3w3Xhp#2573731 - Fraser

0

我也非常喜欢#1。

此外,我真的很喜欢在条件语句中分配变量

例如:

if ( !$userName = $user->login() ) {
    die('could not log in');
}

echo "Welcome, $username";

通常我发现在第一次编写代码的时候,我会得到一堆混乱的嵌套条件语句,所以通常在第二次处理的时候,我会回头整理一下,尽量减少嵌套的条件语句。
除了更整洁外,我发现不需要在脑海中跟踪分支逻辑的代码在概念上更容易理解。
对于无法删除的包含大量过程代码的分支逻辑,我通常会将其放入一个函数/类方法中 - 最好能够在一个屏幕上看到所有正在进行的分支逻辑,这样修改操作或逻辑都不会影响其他部分。

尽管如此,您最终仍可能面临应用程序处于不一致状态或存在资源问题的问题。此外,在生产系统中,您通常不能像那样轻易地退出 - 错误消息和备选流程通常是必需的。 - AJ.
并且if ( !$userName = $user->login()){ 应该改为 if( false != ($userName = $user->login()) ){ - RobertPitt
@AJ: 当然。使用die()只是为了填补空白。Robert: 不,如果我的login()方法在失败时返回false,在成功登录时返回非空字符串,那么它就能按预期工作。 - Jhong

-3

构建条件逻辑的最佳方式是遵循逻辑本身。

如果您有依赖关系,例如第一个条件失败将使其他条件无关紧要,则可以使用returngoto、嵌套条件和异常等方法。

如果您要根据测试做出决策,请说:

if (!isset($_GET['id'])) { 
  //listing part: 
} else { 
  // form displaying part: 
}  

这是关于编程的内容,涉及到 elseelseifcase 等领域。

首先确定你的程序逻辑,然后编写它的逻辑。

trigger_error() 与条件无关。它是一个调试功能,与程序逻辑无关。


你的回答是自圆其说的(最好的逻辑结构方式就是逻辑本身),甚至没有解决问题。此外,建议使用“goto”来帮助构建条件逻辑,给你打负分! - Fraser
我同意Fraser的观点 - 这并不是一个真正的答案。此外,在大多数现代编程语言中,<code>GOTO</code>的时间大多是旧语言的遗留问题,而不是一个好的方法。 - AJ.

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