假设我有这样的伪代码:

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语句可以以某种方式使用吗?


当前回答

你只要这样做。

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?这样的问题会让我失眠,所以我不能给你建议。

希望能有所帮助。

其他回答

在c++中(这个问题在C和c++中都有标记),如果你不能改变函数来使用异常,你仍然可以使用异常机制,如果你写一个像这样的小助手函数

struct function_failed {};
void attempt(bool retval)
{
  if (!retval)
    throw function_failed(); // or a more specific exception class
}

然后你的代码可以如下所示:

try
{
  attempt(executeStepA());
  attempt(executeStepB());
  attempt(executeStepC());
}
catch (function_failed)
{
  // -- this block intentionally left empty --
}

executeThisFunctionInAnyCase();

如果你喜欢花哨的语法,你可以通过显式强制转换来让它工作:

struct function_failed {};
struct attempt
{
  attempt(bool retval)
  {
    if (!retval)
      throw function_failed();
  }
};

然后您可以将代码编写为

try
{
  (attempt) executeStepA();
  (attempt) executeStepB();
  (attempt) executeStepC();
}
catch (function_failed)
{
  // -- this block intentionally left empty --
}

executeThisFunctionInAnyCase();

Don't. Sometimes you need the complexity. The trick is how you do it. Having the "what you do when the condition exists" may take up some room, making the if statement tree appear larger than it really is. So instead of doing things if a condition is set, just set a variable to a specific value for that case( enumeration or number, like 10,014. After the if tree, then have a case statement, and for that specific value, do whatever you would have done in the if tree. It will lighten up the tree. if x1 if x2 if x3 Var1:=100016; endif endif end if case var=100016 do case 100016 things...

在这种情况下,老式的C程序员使用goto。这是goto的一种用法,实际上是Linux样式指南鼓励的,它被称为集中函数exit:

int foo() {
    int result = /*some error code*/;
    if(!executeStepA()) goto cleanup;
    if(!executeStepB()) goto cleanup;
    if(!executeStepC()) goto cleanup;

    result = 0;
cleanup:
    executeThisFunctionInAnyCase();
    return result;
}

有些人使用goto的方法是将body包装成一个循环并将其断开,但实际上这两种方法做的是同一件事。如果你只在executeStepA()成功时才需要一些其他的清理,那么goto方法会更好:

int foo() {
    int result = /*some error code*/;
    if(!executeStepA()) goto cleanupPart;
    if(!executeStepB()) goto cleanup;
    if(!executeStepC()) goto cleanup;

    result = 0;
cleanup:
    innerCleanup();
cleanupPart:
    executeThisFunctionInAnyCase();
    return result;
}

使用循环方法,在这种情况下,您将得到两级循环。

你也可以这样做:

bool isOk = true;
std::vector<bool (*)(void)> funcs; //vector of function ptr

funcs.push_back(&executeStepA);
funcs.push_back(&executeStepB);
funcs.push_back(&executeStepC);
//...

//this will stop at the first false return
for (auto it = funcs.begin(); it != funcs.end() && isOk; ++it) 
    isOk = (*it)();
if (isOk)
 //doSomeStuff
executeThisFunctionInAnyCase();

通过这种方式,您可以获得最小的线性增长大小,每次调用+1行,并且易于维护。


编辑:(谢谢@Unda)我不太喜欢,因为在我看来你失去了能见度:

bool isOk = true;
auto funcs { //using c++11 initializer_list
    &executeStepA,
    &executeStepB,
    &executeStepC
};

for (auto it = funcs.begin(); it != funcs.end() && isOk; ++it) 
    isOk = (*it)();
if (isOk)
 //doSomeStuff
executeThisFunctionInAnyCase();

有一种很好的技术,它不需要使用return语句的额外包装器函数(Itjax规定的方法)。它使用了一个do while(0)伪循环。while(0)确保它实际上不是一个循环,而是只执行一次。但是,循环语法允许使用break语句。

void foo()
{
  // ...
  do {
      if (!executeStepA())
          break;
      if (!executeStepB())
          break;
      if (!executeStepC())
          break;
  }
  while (0);
  // ...
}