假设我有这样的伪代码:

bool conditionA = executeStepA();
if (conditionA){
    bool conditionB = executeStepB();
    if (conditionB){
        bool conditionC = executeStepC();
        if (conditionC){
            ...
        }
    }
}

executeThisFunctionInAnyCase();

函数executeStepX当且仅当前一个成功时执行。 在任何情况下,executeThisFunctionInAnyCase函数都应该在最后被调用。 我在编程方面是一个新手,所以很抱歉提出一个非常基本的问题:有没有一种方法(例如在C/ c++中)以代码易读性为代价,避免长if链产生那种“金字塔式代码”?

我知道如果我们可以跳过executeThisFunctionInAnyCase函数调用,代码可以简化为:

bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;

但是约束是executeThisFunctionInAnyCase函数调用。 break语句可以以某种方式使用吗?


当前回答

你可以使用&&(逻辑与):

if (executeStepA() && executeStepB() && executeStepC()){
    ...
}
executeThisFunctionInAnyCase();

这将满足你的两个要求:

executeStep<X>()应该只在前一个成功时才计算(这称为短路计算) executeThisFunctionInAnyCase()将在任何情况下执行

其他回答

假设所需的代码是我目前看到的:

bool conditionA = executeStepA();
if (conditionA){
    bool conditionB = executeStepB();
    if (conditionB){
        bool conditionC = executeStepC();
        if (conditionC){
            ...
        }
    }
}    
executeThisFunctionInAnyCase();

我想说,正确的方法,因为它是最容易阅读和最容易维护的,应该有更少的缩进级别,这是(目前)这个问题的目的。

// Pre-declare the variables for the conditions
bool conditionA = false;
bool conditionB = false;
bool conditionC = false;

// Execute each step only if the pre-conditions are met
conditionA = executeStepA();
if (conditionA)
    conditionB = executeStepB();
if (conditionB)
    conditionC = executeStepC();
if (conditionC) {
    ...
}

// Unconditionally execute the 'cleanup' part.
executeThisFunctionInAnyCase();

这避免了对gotos、异常、dummy while循环或其他困难结构的任何需求,只需继续手头的简单工作。

你只要这样做。

coverConditions();
executeThisFunctionInAnyCase();

function coverConditions()
 {
 bool conditionA = executeStepA();
 if (!conditionA) return;
 bool conditionB = executeStepB();
 if (!conditionB) return;
 bool conditionC = executeStepC();
 if (!conditionC) return;
 }

99乘以100,这是唯一的方法。

永远、永远、永远不要尝试用计算机代码做一些“棘手”的事情。


顺便说一下,我很确定以下是你脑海中实际的解决方案…

continue语句在算法编程中非常重要。(同样,goto语句在算法编程中也很关键。)

在许多编程语言中,你可以这样做:

-(void)_testKode
    {
    NSLog(@"code a");
    NSLog(@"code b");
    NSLog(@"code c\n");
    
    int x = 69;
    
    {
    
    if ( x == 13 )
        {
        NSLog(@"code d---\n");
        continue;
        }
    
    if ( x == 69 )
        {
        NSLog(@"code e---\n");
        continue;
        }
    
    if ( x == 13 )
        {
        NSLog(@"code f---\n");
        continue;
        }
    
    }
    
    NSLog(@"code g");
    }

(首先要注意:像这个例子一样的裸块是编写漂亮代码的关键和重要部分,特别是如果你在处理“算法”编程。)

再说一遍,你脑子里就是这么想的,对吧?这是很漂亮的写法,所以你有很好的直觉。

然而,不幸的是,在objective-c的当前版本中(对不起,我不知道Swift)有一个可笑的功能,它检查封闭的块是否是一个循环。

下面是你如何解决这个问题的方法……

-(void)_testKode
    {
    NSLog(@"code a");
    NSLog(@"code b");
    NSLog(@"code c\n");
    
    int x = 69;
    
    do{
    
    if ( x == 13 )
        {
        NSLog(@"code d---\n");
        continue;
        }
    
    if ( x == 69 )
        {
        NSLog(@"code e---\n");
        continue;
        }
    
    if ( x == 13 )
        {
        NSLog(@"code f---\n");
        continue;
        }
    
    }while(false);
    
    NSLog(@"code g");
    }

所以不要忘记…

do {} while(false);

只是意味着“做这个块一次”。

也就是说,写do{}while(false)完全没有区别;简单地写{}。

这现在工作完美,因为你想要…这是输出…

所以,这可能就是你在脑海中看到的算法。你应该试着把脑子里的东西写下来。(尤其是你不清醒的时候,因为那个时候你的美就出来了!:))

在“算法”项目中,这种情况经常发生,在objective-c中,我们总是有一个宏…

#define RUNONCE while(false)

... 然后你可以这样做…

-(void)_testKode
    {
    NSLog(@"code a");
    int x = 69;
    
    do{
    if ( x == 13 )
        {
        NSLog(@"code d---\n");
        continue;
        }
    if ( x == 69 )
        {
        NSLog(@"code e---\n");
        continue;
        }
    if ( x == 13 )
        {
        NSLog(@"code f---\n");
        continue;
        }
    }RUNONCE
    
    NSLog(@"code g");
    }

有两点:

首先,尽管objective-c检查continue语句所在的块类型很愚蠢,但“对抗它”很麻烦。所以这是一个艰难的决定。

第二,在这个例子中,你是否应该缩进那个block?这样的问题会让我失眠,所以我不能给你建议。

希望能有所帮助。

似乎你想在一个block中完成所有的调用。 正如其他人所建议的那样,你应该使用while循环并使用break离开,或者使用一个可以使用return离开的新函数(可能更干净)。

我个人排斥goto,即使是函数退出。在调试时很难发现它们。

一个适合您的工作流的优雅替代方法是构建一个函数数组并在此基础上迭代。

const int STEP_ARRAY_COUNT = 3;
bool (*stepsArray[])() = {
   executeStepA, executeStepB, executeStepC
};

for (int i=0; i<STEP_ARRAY_COUNT; ++i) {
    if (!stepsArray[i]()) {
        break;
    }
}

executeThisFunctionInAnyCase();

在阅读了所有的答案之后,我想提供一种新的方法,它在适当的情况下可能非常清晰易读:状态模式。

如果你将所有的方法(executeStepX)打包到一个对象类中,它可以有一个属性getState()

class ExecutionChain
{
    public:
        enum State
        {
          Start,
          Step1Done,
          Step2Done,
          Step3Done,
          Step4Done,
          FinalDone,
        };
        State getState() const;

        void executeStep1();
        void executeStep2();
        void executeStep3();
        void executeStep4();
        void executeFinalStep();
    private:
        State _state;
};

这将允许你将你的执行代码平铺成这样:

void execute
{
    ExecutionChain chain;

    chain.executeStep1();

    if ( chain.getState() == Step1Done )
    {
        chain.executeStep2();
    }

    if ( chain.getState() == Step2Done )
    {
        chain.executeStep3();
    }

    if ( chain.getState() == Step3Done )
    {
        chain.executeStep4();
    }

    chain.executeFinalStep();
}

通过这种方式,它易于阅读,易于调试,你有一个清晰的流控制,还可以插入新的更复杂的行为(例如,只有在至少执行Step2时才执行Special Step)……

我的问题与其他方法,如ok = execute();如果(execute()),则代码应该清晰易读,就像正在发生的事情的流程图一样。在流程图中有两个步骤:1。2执行。基于结果的决定

因此,您不应该将重要的重载方法隐藏在if语句或类似语句中,它们应该独立存在!

另一种解决方案是通过宏hack定义习语。

 #define block for(int block = 0; !block; block++)

现在,“block”可以用break退出,与for(;;)和while()循环的方式相同。例子:

int main(void) {

    block {
       if (conditionA) {
          // Do stuff A...
          break; 
       }
       if (conditionB) {
          // Do stuff B...
          break; 
       }
       if (conditionC) {
          // Do stuff C...
          break; 
       }
       else {
         // Do default stuff...
       }
    } /* End of "block" statement */
    /* --->   The "break" sentences jump here */

    return 0;
} 

尽管使用了“for(;;)”结构,但“block”语句只执行了一次。 这些“块”可以用断句退出。 因此,if else if else if…避免使用句子。 最多,最后一个else可以挂在“块”的末尾,以处理“默认”情况。

该技术旨在避免典型的和丑陋的做{…} while(0)方法。 在宏块中,它定义了一个同样名为block的变量,该变量以这样一种方式定义,即恰好执行了一次for迭代。根据宏的替换规则,宏块定义中的标识符块不会被递归替换,因此block成为程序员无法访问的标识符,但在内部可以很好地控制for(;;)循环的“隐藏”。

此外:这些“块”可以嵌套,因为隐藏变量int块将有不同的作用域。