关于if块的独占性

时间:2018-06-02 11:55:07

标签: c++ c

我对良好的编码习惯有疑问。我理解执行<form method="post" enctype="multipart/form-data"> {% csrf_token %} {{form.as_p}} <input value="submit" type="submit"> </form> 和多个if-else if之间的区别(即,当if中满足条件时,将跳过其余检查)。我在这些方面找到了一段代码:

if-else if

据我所知,如果A == 5,此代码不会检查B == 7.代码有效,这意味着B只有7,如果A不是5,但我认为这只是等待中断当代码改变时。我会做的是:

if (A == 5) {
    do_something();
} else if (B == 7) {
    do_something_else();
}

我的问题是,当我有多个依赖于不同的独占变量的独占案例时,解决流量控制的最佳方法是什么?我的印象是第一个代码(使用else ifs)很大程度上取决于其他代码片段的工作,而其他方面的更改可能会破坏它。第二个看起来有点笨拙。一个开关可能是第三个选项,但我需要创建另一个结构来保持大小写和逻辑来分配它的值,我认为它会有点笨拙和反直觉。

5 个答案:

答案 0 :(得分:2)

您询问了“独家”案例,但条件A == 5B == 7的问题在于它们独占;他们是独立的。

为了完全普遍,你可能需要测试和处理所有四种情况:

if(A == 5) {
    if(B == 7) {
        /* case 1 */
    } else {
        /* case 2 */
    }
} else {
    if(B == 7) {
        /* case 3 */
    } else {
        /* case 4 */
    }
}

这是臭名昭着的“浓密”if / else块。这是臭名昭着的,因为它几乎不可能立即让读者无法遵循,特别是如果涉及案件,或引入更多层次。 (我认为大多数风格指南会告诉你永远不要使用3级或更高级别的if / else树。我当然会这么说。)

我偶尔会使用这两种选择:

(1)完全解耦案件:

if(A == 5 && B == 7) {
    /* case 1 */
} else if(A == 5 && B != 7) {
    /* case 2 */
} else if(A != 5 && B == 7) {
    /* case 3 */
} else if(A != 5 && B != 7) {
    /* case 4 */
} else {
    /* can't happen */
}

这里的要点是让后来的读者最清楚地知道哪些情况与案例1,2,3和4一致。因此,您最好明确列出最后一个else if(A != 5 && B != 7)案例(正如我所示),尽管到那时它基本上是一个“别的”。

(2)转换“两级”开关。我不能说这是一种常见的技巧;它有一种“太聪明”的味道,但它的健壮性和可读性:

#define PAIR(b1, b2) (((b1) << 8) | (b2))

switch(PAIR(A == 5), (B == 7)) {
    case PAIR(TRUE, TRUE):
        /* case 1 */
        break;

    case PAIR(TRUE, FALSE):
        /* case 2 */
        break;

    case PAIR(FALSE, TRUE):
        /* case 3 */
        break;

    case PAIR(FALSE, FALSE):
        /* case 4 */
        break;
}

当条件为A == 5B == 7时,我不建议这样做,因为当你在开关中瘫痪时,“TRUE”和“FALSE”的含义并不明显,但有时候,这种事情可以干净利落地阅读。它也可以干净地适应3层或更多级别的嵌套,不像“浓密”的if / else树,正如我所说的那样,这是非常难以理解的。

答案 1 :(得分:1)

我想您知道,这两段代码并不相同。 (它们等同于 IF 它们都包含“返回或继续或中断”,这使得问题更有趣,但这是一个不同的答案。)

一般来说,你选择哪一个(或你如何选择重写它)必须完全依赖你希望程序做什么。

写作时

if (A == 5) {
    do_something();
} else if (B == 7) {
    do_something_else();
}

除非 do_something_else不等于5,否则你还要说A 。这可能就是你想要的,或者它可能是一个bug 。如果你想在没有else的情况下达到相同的效果,它必须如下所示:

if (A == 5) {
    do_something();
}

if (A != 5 && B == 7) {
    do_something_else();
}

另一方面,您在问题中写的第二段代码有可能同时执行do_something do_something_else

一般来说,如果if / else链中的所有条件都在相同条件下进行变化,而不是涉及A和{{1}的一些异常混合,那么它是最好的(最清晰且最不容易混淆) }}

当替代品真正且刻意排除时,您使用if / else块,并且当您想强调这一事实时。您可以选择使用单独的B块(不与if链接),当替代品不是排他性的,或者只是巧合或意外排除时。例如,我故意编写像

这样的代码
else

当两个事物彼此无关时,我可能会这样做,这意味着在程序逻辑的某些未来版本中,它们可能根本不是排他性的。或者,我可能会这样做,如果if(A == 5) { do_something(); } if(A != 5) { do_some_unrelated_thing(); } 不是一个单一的,但是是一个冗长的,精心设计的块,最后我担心读者可能不记得我们为什么或不做一些东西,另一方面,我们可能想做别的事情。出于类似的原因,我偶尔会写

do_something

在这种情况下,要完成的两件事情彼此无关,而且这样做的原因可能会有所不同。

答案 2 :(得分:1)

最强大的编程方式,
同时避免假设A==5B==7要考虑所有四种情况:

if ((A == 5) && (B == 7))
{
    do_somethingAB();
    /* or */
    do_somethingA();
    do_somethingB();
} else if (A == 5)
{
    do_somethingA();
} else if (B == 7)
{
    do_somethingB();
} else
{
    do_somethingNeither();
    /* or
       do nothing */
}

答案 3 :(得分:1)

[这是我的第三个答案。事实上,我一直在误读你的问题,而没有理解你所提出的要点,这表明我可能根本就不应该回答。]

我认为您提出的基本要点涉及案例独立的情况,您获得else效果由于每个子句都包含一个控制流语句,其中包括:break,或continue,或return,或类似这一点。

在这种特定情况下,我今天的偏好是不使用else。我们写的时候

if(A == 5) {
    do_something();
    return or continue or break;
}

if(B == 7) {
    do_something_else();
    return or continue or break;
}
很明显,这两个条件之间没有任何关系,除了这两个条件之外,他们都做了一些事情来完成&#34;完成&#34;完成子任务,并留下负责执行该子任务的代码块。

当我们分别写两个案例(没有其他)时,我们不仅明确表示他们是独立的,而且可以重新排序,或者可以在他们之间引入另一个案例,等等。 / p>

但是,再次,可以重新排序?两个案例A == 5B == 7两者都是真的有多大可能性?在这种情况下,do_somethingdo_something_else相比,有多重要?如果不能重新排序这两种情况,如果首先测试B并且可能do_something_else进行测试是错误的,我认为明确的else是优选的,以便将两者联系起来将案件放在一起,并使A首先被测试的要求更加明确。

与任何风格问题一样,支持和反对这种事情的论据最终都是非常主观的。你不可能以某种方式找到一个令人难以置信的令人信服的答案。

答案 4 :(得分:1)

One way to handle this is to use a do { ... } while (0); technique.

Here is your original code:

if (A == 5) {
    do_something();
} else if (B == 7) {
    do_something_else();
}

Doing else if on the same line is [IMO] a bit of a hack because it hides the true indentation:

if (A == 5) {
    do_something();
}
else
    if (B == 7) {
        do_something_else();
    }

Using the aformentioned technique, which I've used quite a lot is:

do {
    if (A == 5) {
        do_something();
        break;
    }

    if (B == 7) {
        do_something_else();
        break;
    }
} while (0);

This becomes even more evident when we increase the number of levels in the if/else ladder:

if (A == 5) {
    do_something();
} else if (B == 7) {
    do_something_else();
} else if (C == 9) {
    do_something_else_again();
} else if (D == 3) {
    do_something_for_D();
}

Once again, this is indented to:

if (A == 5) {
    do_something();
}
else
    if (B == 7) {
        do_something_else();
    }
    else
        if (C == 9) {
            do_something_else_again();
        }
        else
            if (D == 3) {
                do_something_for_D();
            }

Using the do/while/0 block, we get something that is simpler/cleaner:

do {
    if (A == 5) {
        do_something();
        break;
    }

    if (B == 7) {
        do_something_else();
        break;
    }

    if (C == 9) {
        do_something_else_again();
        break;
    }

    if (D == 3) {
        do_something_for_D();
        break;
    }
} while (0);

Note: I've been programming in c for 35+ years, and I've yet to find a case where a more standard use of do/while (e.g. do { ... } while (<cond>)) can't be replaced more cleanly/effectively with either a standard for or while loop. Some languages don't even have a do/while loop. Thus, I consider the do loop to be available for reuse.


Another use of do/while/0 is to allow things defined by a preprocessor macro to appear as a single block:

#define ABORTME(msg_) \
    do { \
        printf(stderr,"ABORT: %s (at line %d)\n",msg_,__LINE__); \
        dump_some_state_data(); \
        exit(1); \
    } while (0)

if (some_error_condition)
    ABORTME("some_error_condition");