Goto 和代码重复 - 在这种情况下它们可以避免吗?

时间:2021-07-09 15:47:34

标签: c loops goto

我最近遇到了一个编程问题,在我看来,最优化的解决方法是使用 goto,尽管这不是一个好的做法。问题是:告诉用户输入一个正自然数(> 0)并读取输入。如果这个数字有效,告诉用户那个数字的平方。在输入正确时执行此操作。我想出了一些解决方案,但它们似乎都有问题。以下是其中两个:
解决方案 1 - 问题:使用 goto

func getMoviees(){
    MovieServiceAPI.shared.fetchMovies(from: "https://api.themoviedb.org/3/movie/now_playing") { (moviesResponse) in
        guard let safeMovies = moviesResponse?.results else {return}
        self.movieList = safeMovies
        
        DispatchQueue.main.async { [unowned self] in
            hideLoader()
            self.tableView.reloadData()
        }
    }
}

解决方案 2 - 问题:仔细检查 num > 0(代码重复)

#include <stdio.h>

int main()
{
    int num;

_LOOP:
    printf("Enter a positive natural number: ");
    scanf("%i", &num);

    if (num > 0) {
        printf("Square: %i\n", num * num);
        goto _LOOP;
    }

    printf("Invalid number\n");

    return 0;
}

显然,有更多的方法可以解决这个问题,但我想出的所有其他方法都没有使用 goto 遇到相同的代码重复问题。那么,是否有避免 goto 和代码重复的解决方案?如果没有,我应该去哪一个?

5 个答案:

答案 0 :(得分:2)

这是答案的一半;尝试填补缺失的部分。请记住,有时最好将循环构建为“做某事直到……”而不是“在……期间做某事”

    for (;;) {
        printf("Enter a positive natural number: ");
        scanf("%i", &num);
        if (num <= 0)
            break;
        printf("Square: %i\n", num * num);
    }
    printf("Invalid number\n");

[更新@rdbo 的回答]

答案 1 :(得分:0)

跳出循环怎么样?这基本上是循环结束的 goto 语句,没有明确使用 goto

#include <stdio.h>

int main()
{
    int num;

    while(1) {
        printf("Enter a positive natural number: ");
        scanf("%i", &num);

        if (num > 0) {
            printf("Square: %i\n", num * num);
        } else {
            printf("Invalid number\n");
            break;
        }
    }

    return 0;
}

答案 2 :(得分:0)

对于初学者来说,如果您期望一个非负数,那么变量 num 应该具有无符号整数类型,例如 unsigned int

正如您的问题中所写,用户可以输入无效数据或中断输入。你必须处理这种情况。

乘法 num * num 也可能导致溢出。

使用 goto 而不是循环确实是个坏主意。

注意你应该在使用变量的最小范围内声明变量。

为这样的任务使用 for 循环也是一个坏主意。使用 while 循环更具表现力。

程序可以如下所示

#include <stdio.h>
#include <stdbool.h>

int main(void) 
{
    while ( true )
    {
        printf( "Enter a positive natural number: " );
        
        unsigned int num;

        if ( scanf( "%u", &num ) != 1 || num == 0 ) break;

        printf( "Square: %llu\n", ( unsigned long long )num * num );
    }

    puts( "Invalid number" );

    return 0;
}

程序输出可能看起来像

Enter a positive natural number: 100000000
Square: 10000000000000000
Enter a positive natural number: 0
Invalid number

或者将最后一个输出语句移到while语句中会更好。例如

#include <stdio.h>
#include <stdbool.h>

int main(void) 
{
    while ( true )
    {
        printf( "Enter a positive natural number: " );
        
        unsigned int num;

        if ( scanf( "%u", &num ) != 1 || num == 0 )
        {
            puts( "Invalid number" );
            break;
        }
        
        printf( "Square: %llu\n", ( unsigned long long )num * num );
    }

    return 0;
}

答案 3 :(得分:0)

另一个选项:如果满足继续条件,则检查存储的布尔值。它比无限循环/中断方法(对我而言)更容易阅读,并且没有代码重复。

#include <stdio.h>
#include <stdbool.h>

int main()
{
    int num;
    bool bContinue;

    do {
        printf("Enter a positive natural number: ");
        scanf("%i", &num);

        if (num > 0){
            printf("Square: %i\n", num * num);
            bContinue = true;
        }
        else{
            printf("Invalid number\n");
            bContinue = false;
        }
    } while (bContinue);             

    return 0;
}

答案 4 :(得分:0)

我有点惊讶还没有人建议功能分解。 与其编写一大堆原始语句,不如将 main 切成更小的函数。 除了可读性/可维护性优势外,它还有助于以非常自然的方式消除代码重复。

在 OP 的情况下,从最终用户那里获取输入是一个单独的责任,并且为单独的功能做出了不错的选择。

static bool user_enters_number(int *ptr_to_num)
{
    printf("Enter a positive natural number: ");
    return scanf("%i", ptr_to_num) == 1;
}

注意 user_enters_number 显式测试 scanf 的返回值。 这改进了文件结束处理。

同样,您可以赋予数字验证自己的功能。 这可能看起来有点矫枉过正(它只是 num > 0,对吗?),但它让我们有机会将验证与生成的错误消息结合起来。 在 main 末尾打印“Invalid number”感觉不对。无效数字不是唯一的退出条件;文件结尾是另一个。 因此,我将让验证函数确定消息。 作为奖励,这使得支持多种错误类型成为可能(例如,负数和零的单独消息)。

static bool is_valid_number(int num)
{
    bool ok = (num > 0);
    if (!ok) printf("Invalid number\n");
    return ok;
}

我们现在有两个布尔类型的函数,它们可以用 && 整齐地链接在一起并放在循环的条件部分内,这是一种惯用的说法:如果这些函数中的任何一个失败(即返回false),立即退出循环。

剩下的是一个非常干净的 main 函数。

int main(void)
{
    int num;
    while (user_enters_number(&num) && is_valid_number(num))
    {
        printf("Square: %i\n", num * num);
    }
}

要了解可维护性方面的好处,请尝试重写此代码,使其接受两个数字并打印它们的乘积。

int main(void)
{
    int num1, num2;
    while (user_enters_number(&num1) && is_valid_number(num1) &&
           user_enters_number(&num2) && is_valid_number(num2))
    {
        printf("Product: %i\n", num1 * num2);
    }
}

更改是微不足道的,并且仅限于单个功能 (尽管您可能会考虑将参数 input_prompt 添加到 user_enters_number)。

这种“分而治之”的方法没有性能损失:智能编译器会做任何必要的事情来优化代码,例如内联函数。

相关问题