没有goto跳转进出循环的更好方法?

时间:2014-09-11 22:02:45

标签: c++ c goto

do {
    int j, k;
    if (num_copy_array[0][0] == num_copy_array[0][1]) {
        goto next_num;
    } else {
        goto first_time;
    }
    for (j = 0; j < ARRAY_LEN; j++) {
        for (k = 0; k < NUM_LEN; k++) {
            goto skip;
        first_time:
            j = 0, k = 2;
        skip:
            for (int j_2 = 0; j_2 <= j; j_2++) {
                for (int k_2 = 0; k_2 <= NUM_LEN; k_2++) {
                    if (j_2 == j && k_2 == k) {
                        goto next_digit;
                    }
                    if (num_copy_array[j][k] == num_copy_array[j_2][k_2]) {
                        goto next_num;
                    }
                }
            }
        next_digit:;
        }
    }
} while (false);
/*
 * some
 * code
*/
:next_num // starts the bigger loop outside

这是我在编写c ++程序时进行特殊循环入口并打破多个循环的结果。我必须将循环变量'j,k'声明为外部,以便在使用goto跳转到循环之前声明它们,并使用假的do循环来本地化它们。

很多人都说“从不使用goto”,我同意使用不必要的getos会产生一个不好的程序。但是在这种情况下,我很难想到一个替代方法,而不是使用临时切换变量并在每个循环入口处检查它们,这种方式我认为效率非常低且难以阅读。

你能想出一个很好的方法让我的程序更好,或者你有一般建议吗?

4 个答案:

答案 0 :(得分:9)

Short anwer

将代码分解为函数。您可以明智地使用辅助方法和快速返回来替换这些goto。辅助方法是管理四重嵌套循环和蛇形goto的复杂性的好方法。他们还将通过为各种运动部件命名来揭示算法的基本逻辑。

答案很长:

由于@MooingDuck在他的评论中破译,此代码的唯一外部效果是正常退出循环或跳转到next_num标签退出。

考虑到这一点,第一步是将循环移动到一个函数,该函数返回truefalse,控制是否执行“某些代码”。

if (should_execute_some_code()) {
    /*
     * some
     * code
     */
}

// starts the bigger loop outside

在函数内部,我们会将任意goto next_num行转换为return true,否则return false如果控件从函数底部掉落。

bool should_execute_some_code()
{
    int j, k;
    if (num_copy_array[0][0] == num_copy_array[0][1]) {
        return true;
    }

    goto first_time;

    for (j = 0; j < ARRAY_LEN; j++) {
        for (k = 0; k < NUM_LEN; k++) {
            goto skip;
        first_time:
            j = 0, k = 2;
        skip:
            for (int j_2 = 0; j_2 <= j; j_2++) {
                for (int k_2 = 0; k_2 <= NUM_LEN; k_2++) {
                    if (j_2 == j && k_2 == k) {
                        goto next_digit;
                    }
                    if (num_copy_array[j][k] == num_copy_array[j_2][k_2]) {
                        return true;
                    }
                }
            }
        next_digit:;
        }
    }

    return false;
}

这是一个开始。现在我们怎样摆脱其余的东西呢?我们来处理那个first_time标签。无需无条件地跳入循环内部。它不漂亮,但我们可以用k的条件初始化替换跳转。在第一次迭代时将其设置为2,之后将其设置为0。这让我们摆脱了first_timeskip标签。

bool should_execute_some_code()
{
    int j = 0, k = 2;

    if (num_copy_array[0][0] == num_copy_array[0][1]) {
        return true;
    }

    for (int j = 0; j < ARRAY_LEN; j++) {
        for (int k = (j == 0 ? 2 : 0); k < NUM_LEN; k++) {
            for (int j_2 = 0; j_2 <= j; j_2++) {
                for (int k_2 = 0; k_2 <= NUM_LEN; k_2++) {
                    if (j_2 == j && k_2 == k) {
                        goto next_digit;
                    }
                    if (num_copy_array[j][k] == num_copy_array[j_2][k_2]) {
                        return true;
                    }
                }
            }
        next_digit:;
        }
    }

    return false;
}

接下来是next_digit标签。如果j == j_2k == k_2想要停止内部两个循环。这可以通过更具选择性的循环条件而不是goto来控制。

bool should_execute_some_code()
{
    if (num_copy_array[0][0] == num_copy_array[0][1]) {
        return true;
    }

    for (int j = 0; j < ARRAY_LEN; j++) {
        for (int k = (j == 0 ? 2 : 0); k < NUM_LEN; k++) {
            for (int j_2 = 0; j_2 <= j; j_2++) {
                for (int k_2 = 0; k_2 <= NUM_LEN && !(j_2 == j && k_2 == k); k_2++) {
                    if (num_copy_array[j][k] == num_copy_array[j_2][k_2]) {
                        return true;
                    }
                }
            }
        }
    }

    return false;
}

此时,在我看来,你正在检查二维数组中的任何条目是否等于二维数组中的任何其他条目。而且你要小心忽略检查同一个单元格。好吧。现在让我们知道它的功能,重命名这个功能。让我们通过引入第二个辅助函数来拆分四个嵌套循环。

bool has_duplicate_entry()
{
    if (num_copy_array[0][0] == num_copy_array[0][1]) {
        return true;
    }

    for (int j = 0; j < ARRAY_LEN; j++) {
        for (int k = (j == 0 ? 2 : 0); k < NUM_LEN; k++) {
            if (contains_entry(num_copy_array[j][k], j, k)) {
                return true;
            }
        }
    }

    return false;
}

bool contains_entry(int entry, int not_j, int not_k)
{
    for (int j = 0; j <= not_j; j++) {
        for (int k = 0; k <= NUM_LEN && !(j == not_j && k == not_k); k++) {
            if (num_copy_array[j][k] == entry) {
                return true;
            }
        }
    }

    return false;
}

答案 1 :(得分:3)

我建议像(更好的名字):

bool bar(int j, int k)
{
    for(int j_2 = 0; j_2 <= j; j_2++) {
        for(int k_2 = 0; k_2 <= NUM_LEN; k_2++) {
            if(j_2 == j && k_2 == k) {
                return true;
            }
            if(num_copy_array[j][k] == num_copy_array[j_2][k_2]) {
                return false;
            }
        }
    }
    return true;
}

bool foo()
{
    if(num_copy_array[0][0] == num_copy_array[0][1]) {
        return false;
    }
    for(int j = 0; j < ARRAY_LEN; j++) {
        for(int k = (j == 0) ? 2 : 0; k < NUM_LEN; k++) {
            if (bar(j, k) == false) {
                return false;
            }
        }
    }
    return true;
}

void foobar()
{
    if (foo()) {
    /*
     * some
     * code
    */
    }
    // Other code
}

答案 2 :(得分:1)

您可以在循环中添加另一项检查,例如:

for (i=0; i < x && !ready; i++)
{
    for (j = 0; j < y && !ready; j++)
    {
        // Now you can maintain the ready variable to finish...
        if (whatever)
            ready = true; // Or, ready = NULL Or ready > 0 etc (whatever suits your situation) 
    }
}

如果您可以将代码分解为更多函数,那么不需要那么多可能有帮助的嵌套循环。

答案 3 :(得分:1)

编程元素中,Stepanov和McJones提供了一个优雅编码的状态机,即使使用goto s。他们提到明显的非goto版本并不那么优雅。 Knuth花费了 Literate Programming 的一章来争论偶尔使用goto。我不是goto的粉丝,但我接受它有它的用途。但是,我总是尝试使用任何代码的非goto版本。

在这种情况下,我认为你正在尝试simulate named loops,但我认为你不需要。例如,外部do ... while循环看起来非常像一个残缺的函数,只有在某些变量处于正确状态时才能调用。你可以(并且,我认为,应该)用函数替换它;当你这样做时,你会注意到你不再需要goto skipfirst_time

我开始尝试重构代码,但不知道你真正想做什么,我无法做太多。