问题描述
我最近遇到了一个编程问题,在我看来,最优化的解决方法是使用 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 和代码重复的解决方案?如果没有,我应该去哪一个?
解决方法
这是答案的一半;尝试填补缺失的部分。请记住,有时最好将循环构建为“做某事直到……”而不是“在……期间做某事”
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 的回答]
,跳出循环怎么样?这基本上是循环结束的 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;
}
,
对于初学者来说,如果您期望一个非负数,那么变量 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;
}
,
另一个选项:如果满足继续条件,则检查存储的布尔值。它比无限循环/中断方法(对我而言)更容易阅读,并且没有代码重复。
#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;
}
,
我有点惊讶还没有人建议功能分解。
与其编写一大堆原始语句,不如将 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
)。
这种“分而治之”的方法没有性能损失:智能编译器会做任何必要的事情来优化代码,例如内联函数。