我很想知道你对我们在课堂上写的这个算法的看法。我在考虑如何在不使用库函数的情况下优化它(就代码/复杂性的大小而言)(除了用于读写的<iostream> )。
任务(翻译):
关于闰年的公历规则是在1582年10月15日(星期五)推出的。编写一个程序,当以“dd.MM.yyyy”格式指定日期时,该程序将显示当天的名称。您需要定义并使用枚举类型
Weekday{星期一、星期二、…、星期日}。
这是我的代码:
#include <iostream>
bool isLeapYear(int year){
if(year % 4 == 0 && year % 100 != 0){
return true;
} else if (year % 400 == 0){
return true;
} else {
return false;
}
}
enum Weeknday {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
int main() {
// Get date
int day,mounth,year;
scanf("%d.%d.%d",&day,&mounth,&year);
// Count days
int days = 0;
for(int i = 1582; i <= year; i++){
if(i != year) {
if(isLeapYear(i)){
days += 366;
} else {
days += 365;
}
} else {
for(int j = 1; j <= mounth; j++){
if(j != mounth){
if(j == 1 || j == 3 || j == 5 || j == 7 || j == 8 || j == 10 || j == 12){
days += 31;
} else if (j == 4 || j == 6 || j == 9 || j == 11){
days += 30;
} else if (j == 2){
if(isLeapYear(i)){
days += 29;
} else {
days += 28;
}
}
} else {
days += day;
}
}
}
}
int a = (days-3)%7;
Weeknday enum_day;
switch(a) {
case 1:
enum_day = Monday;
std::cout << "Monday";
break;
case 2:
enum_day = Tuesday;
std::cout << "Tuesday";
break;
case 3:
enum_day = Wednesday;
std::cout << "Wednesday";
break;
case 4:
enum_day = Thursday;
std::cout << "Thursday";
break;
case 5:
enum_day = Friday;
std::cout << "Friday";
break;
case 6:
enum_day = Saturday;
std::cout << "Saturday";
break;
case 7:
enum_day = Sunday;
std::cout << "Sunday";
break;
}
std::cout << enum_day;
}发布于 2022-10-17 16:02:39
几乎所有东西,包括输入和输出,都在main()函数中。这使得很难为代码创建自动测试。我建议创建一个无需任何I/O即可测试的函数:
int weekday(int year, int month, int day)然后,main()可以完成输入的读取和输出的写入。
我们需要包括<cstdio>来定义std::scanf。不需要包含(并使用普通scanf())的情况下,您可以在平台上构建它,这并不意味着它将在其他地方构建,因为这是因为编译器所做的可选选择并不是标准C++强制要求的。
当我们使用std::scanf()时,我们绝对必须检查它转换了多少值,否则我们将使用统一变量。以下是一个建议:
// Get date
int day, month, year;
if (std::scanf("%d.%d.%d", &day, &mounth, &year) != 3) {
std::cerr << "Input must be in 'day.month.year' format.\n";
return EXIT_FAILURE;
}(我们需要包括<cstdlib>来定义EXIT_FAILURE)。
在打印日期名称的switch中,我们有一个永远无法到达的case 7。我想那是case 0的本意。
我们可以使用一个日期名称数组,而不是switch。
如果我们将Weekday枚举定义为以Sunday开头(这将等于零),则可以将剩余的天数直接转换为Weekday (int和enum类型是可转换的)。如果不允许更改顺序,则可以更改算法以将值减少1。
总是生产完整的生产线输出。每一行都应该以'\n'结尾。
拼写:在一个小程序中并不重要,但在较大的程序中(特别是作为团队成员工作时),拼写错误会使搜索代码变得困难。对诸如“月”和“平日”这样的单词,要尽量使用正确的拼写。
该算法可以进行改进。我们可以计算出在不同的年份中增加多少天,而不单独循环(并且注意,我们只关心模块7的天数,所以我们每年只需要计算1或2天)。
现在继续使用迭代算法,让我们来检查它的问题。
如果我们进入不到1582年的一年,我们就会给出一个毫无意义的结果--最好是对此进行测试,并给出一个错误消息。(例如,输入一年中的最后两位数字很可能是用户错误)。
我们有一个循环,测试它是否是最后一次迭代。而不是这样做,使用一个循环与一个少的迭代,并移动最后一个迭代代码后的循环。看起来是这样的:
for(int i = 1582; i < year; i++){
if(isLeapYear(i)){
days += 366;
} else {
days += 365;
}
}
for(int j = 1; j < mounth; j++) {
if(j == 1 || j == 3 || j == 5 || j == 7 || j == 8 || j == 10 || j == 12){
days += 31;
} else if (j == 4 || j == 6 || j == 9 || j == 11){
days += 30;
} else if (j == 2){
if(isLeapYear(year)){
days += 29;
} else {
days += 28;
}
}
}
days += day;这听起来不更合理吗?
“月份”循环中的大if可能更清晰一些,比如switch --或者是数组中的查找。一定要先检查0≤月≤12-更多的错误报告!
这里有一个隐秘的bug:
int a = (days-3)%7;
1582年的头几天将产生负值。这表明了测试“边缘案例”的价值--我们真的应该在第一天就试一试。
我们可以通过编写(days+4) % 7来确保一个正值。或者用值4初始化days (或者更好,使用Thursday,对应于1581-12-31),而不是0,所以我们不需要在最后进行这种调整。
发布于 2022-10-18 14:37:34
关于代码,我能说的最好的事情是,至少它在代码中没有using namespace std;。
不知道为什么要使用std::function来实现这一点,但是,更多的函数会使这段代码更容易读、写、调试和维护。
在C和C++中,数组和枚举默认为零(0),而不是1。这可以用来简化代码。
代码中没有错误检查,任何具有用户输入的代码都需要进行错误检查。代码不会提示用户输入值,也不会指定用户应该使用的格式。
scanf()函数是一个C库函数,最好使用C++输入,如std::cin。可能代码应该输入一个字符串,然后将字符串转换为数字。
通常,switch/case语句应该包括一个不使用适当值时的default大小写。
不清楚为什么创建枚举Weeknday,因为它没有被实际使用。
代码中有很多魔术数字(1、3、5、7、12、30、31、28、29、365、366),最好是为它们创建符号常量,以使代码更加可读性和易于维护。这些数字可以在许多地方使用,只需编辑一行就可以更改它们,从而使维护变得更容易。
代码中的数值常数有时被称为幻数幻数,因为它们没有明显的意义。在堆栈过流上对此进行了讨论。
符号常量用法的一些示例:
constexpr int LEAP_YEAR_COUNT = 366;
constexpr int NORMAL_YEAR_COUNT = 356;
constexpr int LEAP_FEBRUARY_COUNT = 29;
constexpr int FEBRUARY_COUNT = 28;
int main() {
// Get date
int day, mounth, year;
scanf("%d.%d.%d", &day, &mounth, &year);
// Count days
int days = 0;
for (int i = 1582; i <= year; i++) {
if (i != year) {
if (isLeapYear(i)) {
days += LEAP_YEAR_COUNT;
}
else {
days += NORMAL_YEAR_COUNT;
}
}
...在某些情况下,您可以使用条件赋值而不是if then else。
if (i != year) {
days += (isLeapYear(i)) ? LEAP_YEAR_COUNT : NORMAL_YEAR_COUNT;
}有时这被称为三元,在C和C++以及其他一些编程语言中很常见。
https://codereview.stackexchange.com/questions/280514
复制相似问题