“清理”嵌套if语句

时间:2017-12-03 19:14:47

标签: c++ performance file if-statement nested-if

在我正在创建的控制台程序中,我有一些代码可以解析文件。解析每一行后,检查语法错误。如果存在语法错误,程序将停止读取文件并转到程序的下一部分。问题是,它非常混乱,因为到目前为止我唯一的解决方案是一系列嵌套的if语句或一行if语句。嵌套ifs的问题是它非常快速地变得非常混乱,并且一系列if语句对几个不需要测试的事情进行了程序测试。下面是我的问题的一些sudo代码(注意我没有使用return语句)

显示伪代码而不是真实代码,因为它非常大

嵌套if:

open file;
read line;
//Each if is testing something different
//Every error is different
if (line is valid)
{
    read line;
    if (line is valid)
    {
        read line;
        if (line is valid)
        {
            do stuff;
        }
        else
            error;
    }
    else
        error;

}
else
    error;
code that must be reached, even if there was an error;

非嵌套ifs:

bool fail = false;
open file;
read line;
//Each if is testing something different
//Every error is different
if (line is valid)
    read line;
else
{
    error;
    fail = true;
}
if (!error && line is valid)
    read line;
else
{
    error;
fail = true;
}
if (!error && line is valid)
    do stuff;
else
    error;
//Note how error is constantly evaluated, even if it has already found to be false
code that must be reached, even if there was an error;

我查看了许多不同的网站,但他们的判断与我的问题不同。此代码在运行时可以正常工作,但正如您所看到的那样,它不是很优雅。是否有人对我的问题有更可读/更有效的方法?任何帮助表示赞赏:)

3 个答案:

答案 0 :(得分:2)

有两种选择:

选项1:链读取和验证

这与std::istream提取运算符的工作方式类似。你可以这样做:

void your_function() {
  std::ifstream file("some_file");
  std::string line1, line2, line3;
  if (std::getline(file, line1) &&
      std::getline(file, line2) &&
      std::getline(file, line3)) {
    // do stuff
  } else {
    // error
  }
  // code that must be reached, even if there was an error;
}

选项2:分成不同的功能

这可能会有点长,但是如果你把事情分开(并给出一切正确的名字),它实际上可以非常易读和可调试。

bool step3(const std::string& line1,
           const std::string& line2,
           const std::string& line3) {
  // do stuff
  return true;
}

bool step2(std::ifstream& file,
           const std::string& line1,
           const std::string& line2) {
  std::string line3;
  return std::getline(file, line3) && step3(line1, line2, line3);
}

bool step1(std::ifstream& file,
           const std::string& line1) {
  std::string line2;
  return std::getline(file, line2) && step2(file, line1, line2);
}

bool step0(std::ifstream& file) {
  std::string line1;
  return std::getline(file, line1) && step1(file, line1);
}

void your_function() {
  std::ifstream file("some_file");
  if (!step0(file)) {
    // error
  }
  // code that must be reached, even if there was an error;
}

这个示例代码有点过于琐碎。如果在每个步骤中发生的行验证比std::getline的返回值更复杂(在进行实际输入验证时通常就是这种情况),那么这种方法的好处是使其更具可读性。但是如果输入验证就像检查std::getline一样简单,那么第一个选项应该是首选。

答案 1 :(得分:1)

一种常见的现代做法是早期回归RAII。基本上它意味着必须发生的代码应该在类的析构函数中,并且您的函数将具有该类的本地对象。现在当你遇到错误时,你可以从函数中提前退出(使用Exception或只是return),并且该本地对象的析构函数将处理必须发生的代码。

代码看起来像这样:

class Guard
{
...
Guard()
~Guard() { /*code that must happen */}
...
}

void someFunction()
{
    Gaurd localGuard;
    ...
    open file;
    read line;
    //Each if is testing something different
    //Every error is different
    if (!line)
    {
        return;
    }

    read line;
    if (!line)
    {
        return;
    }
    ...
}

答案 2 :(得分:1)

  

我的问题是否有更可读/更有效的方法

步骤1.查看文本解析器的经典示例

答案:一个编译器,它解析文本文件并产生不同类型的结果。

步骤2.阅读一些理论编译器如何工作

有很多方法和技巧。书籍,在线和开源示例。简单而复杂。

当然,如果你不感兴趣,你可能会跳过这一步。

第3步。对你的问题应用理论

通过这个理论,你不会错过像#34;状态机","自动化"以下是维基百科的简要说明:

https://en.wikipedia.org/wiki/Automata-based_programming

维基页面上基本上有一个现成的例子:

#include <stdio.h>
enum states { before, inside, after };
void step(enum states *state, int c)
{
    if(c == '\n') {
        putchar('\n');
        *state = before;
    } else
    switch(*state) {
        case before:
            if(c != ' ') {
                putchar(c);
                *state = inside;
            }
            break;
        case inside:
            if(c == ' ') {
                *state = after;
            } else {
                putchar(c);
            }
            break;
        case after:
            break;
    }
} 
int main(void)
{
    int c;
    enum states state = before;
    while((c = getchar()) != EOF) {
        step(&state, c);
    }
    if(state != before)
        putchar('\n');
    return 0;
}

或者是状态机的C ++示例:

#include <stdio.h>
class StateMachine {
    enum states { before = 0, inside = 1, after = 2 } state;
    struct branch {
        unsigned char new_state:2;
        unsigned char should_putchar:1;
    };
    static struct branch the_table[3][3];
public:
    StateMachine() : state(before) {}
    void FeedChar(int c) {
        int idx2 = (c == ' ') ? 0 : (c == '\n') ? 1 : 2;
        struct branch *b = & the_table[state][idx2];
        state = (enum states)(b->new_state);
        if(b->should_putchar) putchar(c);
    }
};
struct StateMachine::branch StateMachine::the_table[3][3] = {
                 /* ' '         '\n'        others */
    /* before */ { {before,0}, {before,1}, {inside,1} },
    /* inside */ { {after, 0}, {before,1}, {inside,1} },
    /* after  */ { {after, 0}, {before,1}, {after, 0} }
};
int main(void)
{
    int c;
    StateMachine machine;
    while((c = getchar()) != EOF)
        machine.FeedChar(c);
    return 0;
}

当然,你应该用线来代替字符。

这种技术可扩展到复杂的编译器,经过大量实现验证。所以,如果你正在寻找一个&#34;权利&#34;方法,在这里。