这是我在C ++传奇中编写的第一个我实际上认为有用的程序。该作业的说明虽然冗长而平凡:


编写一个输入日期(例如:2008年7月4日)并
输出日期的程序。该日期对应的星期。以下
算法来自Wikipedia。该实现将需要几个功能。




bool isLeapYear(int year);

如果true是a年,则此函数应返回year,如果false是is年,则该函数应返回int getCenturyValue(int year);。它不是。这是确定a年的伪代码:

leap_year = (year divisible by 400) or (year divisible by 4 and not divisible by 100)



int getYearValue(int year);

该函数应采用前两位年份(即世纪)除以\ $ 4 \ $,然后保存余数。从\ $ 3 \ $减去余数并返回该值乘以\ $ 2 \ $。例如,年份\ $ 2008 \ $变为\ $ \ dfrac {20} {4} = 5 \ $,余数为\ $ 0 \ $。 \ $ 3-0 = 3 \ $。返回\ $ 3 \ cdot 2 = 6 \ $。


int getMonthValue(int month, int year);

该函数基于本世纪初以来的年份来计算值。首先,提取年份的最后两位数字。例如,为\ $ 2008 \ $提取\ $ 08 \ $。接下来,考虑leap年。将上一步的值除以\ $ 4 \ $,然后舍弃其余的。将两个结果相加并返回此值。例如,从\ $ 2008 \ $中提取\ $ 08 \ $。然后\ $ \ dfrac {8} {4} = 2 \ $,余下的\ $ 0 \ $。返回\ $ 2 + 8 = 10 \ $。


isLeapYear()

该函数应根据下表返回一个值,并且需要调用getMonthValue函数。

$$
\ newcommand \ T {\ Rule {0pt} {1em} {。5em}}
\ begin {array} {| c | c |}
\ hline \ textbf {Month}和\ textbf {Return Value} \\\ hline
\ text {一月} \ T&0 \ left(6 \ text {如果年份是a年} \ right)\\\ hline
\ text {February} \ T&3 \ left(2 \ text {如果年份是a年} \ right)\\\ hline
\ text {March} \ T&3 \\\ hline
\ text {April} \ T&6 \\\ hline
\ text {May} \ T&1 \\\ hline
\ text {June} \ T&4 \\\ hline
\ text {July} \ T&6 \\\ hline
\ text {八月} \ T&2 \\\ hline
\ text {九月} \ T&5 \\\ hline
\ text {十月} \ T&0 \\\ hline
\ text {11月} \ T&3 \\\ hline
\ text {December} \ T&5 \\\ hline
\ end {array}
$$

最后,要计算星期几,请计算日期的总和加上getYearValuegetCenturyValuevoid返回的值。将总和除以\ $ 7 \ $,然后计算余数。其余的\ $ 0 \ $对应于星期日,\ $ 1 \ $对应于星期一,依此类推,直到\ $ 6 \ $,对应于星期六。例如,2008年7月4日的日期应计算为(每月的某天)+(getMonthValue)+(getYearValue)+(getCenturyValue)= \ $ 4 + 6 + 10 + 6 = 26 \ $。 \ $ \ dfrac {26} {7} = 3 \ $,其余为\ $ 5 \ $。一周的第五天对应于星期五。

您的程序应允许用户输入任何日期,并用英语输出相应的星期几。

该程序应包括一个名为getInputdayOfWeek.cpp函数,该函数
提示用户输入日期,并使用传递引用参数返回月,日和年。您可以选择让用户
以数字(1-12)或月份名称输入日期的月份。


q4312079q:

/**
 * @file dayOfWeek.cpp
 * @brief Computes the day of the week given a certain date
 * @author syb0rg
 * @date 10/23/14
 */

#include <cctype>
#include <iostream>
#include <limits>

enum Months {None, January, February, March, April, May, June, July, August, September, October, November, December};
std::string *weekDays = new std::string[7] {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

/**
 * Makes sure data isn't malicious, and signals user to re-enter proper data if invalid
 */
size_t getSanitizedNum()
{
    size_t input = 0;
    while(!(std::cin >> input))
    {
        // clear the error flag that was set so that future I/O operations will work correctly
        std::cin.clear();
        // skips to the next newline
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        std::cout << "Invalid input.  Please enter a positive number: ";
    }
    return input;
}

/**
 * Safetly grabs and returns a lowercase version of the character (if the lowercase exists)
 */
char32_t getSanitizedChar()
{
    // absorb newline character (if existant) from previous input
    if('\n' == std::cin.peek()) std::cin.ignore();
    return std::tolower(std::cin.get());
}

bool isLeapYear(const int year)
{
    if (0 == (year % 400) || (0 == (year % 4) && (year % 100))) return true;
    else return false;
}

int getCenturyValue(const size_t year)
{
    return (2 * (3 - div(year / 100, 4).rem));
}

int getYearValue(const size_t year)
{
    int mod = year % 100;
    return (mod + div(mod, 4).quot);
}

int getMonthValue(const size_t month, const size_t year)
{
    switch (month) {
        case January:
            if (isLeapYear(year)) return 6;
        case October:
            return 0;
        case May:
            return 1;
        case August:
            return 2;
        case February:
            if (isLeapYear(year)) return 2;
        case March:
        case November:
            return 3;
        case June:
            return 4;
        case September:
        case December:
            return 5;
        case April:
        case July:
            return 6;
        default:
            return -1;
    }
}

int dayOfWeek(const size_t month, const size_t day, const size_t year)
{
    return div(day + getMonthValue(month, year) + getYearValue(year) + getCenturyValue(year), 7).rem;
}

void getInput(size_t &month, size_t &day, size_t &year)
{
    std::cout << "Enter the month (1-12): ";
    month = getSanitizedNum();

    std::cout << "Enter the day (1-31): ";
    day = getSanitizedNum();

    std::cout << "Enter the year: ";
    year = getSanitizedNum();
}

int main()
{
    size_t month = 0;
    size_t day = 0;
    size_t year = 0;
    do
    {
        getInput(month, day, year);
        std::cout << "The day of the week is " << weekDays[dayOfWeek(month, day, year)] << std::endl;

        std::cout << "Run the program again (y/N): ";  // signify n as default with capital letter
    } while ('y' == getSanitizedChar());
}


评论

实际上,您永远不想自己编写时间库的核心功能。要获得正确的答案,它们非常复杂,而今天正确的东西明天可能就不正确了。如果这不是家庭作业,则建议学习使用Boost.Date_Time。

@Chuu很好。第一次看到这个例子时,我感到非常惊讶。我从来不认为告诉时间会如此复杂,以至于无法真正解决类似情况。我很高兴那些时间图书馆为我照顾到了!

1582年之前没有leap年。假定为阳历。

“此功能应采用年份的前两位数字(即世纪)”(不一定正确)(实际上,在大多数年份中,这是错误的)。

这种代码很疯狂。

#1 楼

这在某些方面是不正确的:


std::string *weekDays = new std::string[7] {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};



这不仅是全局变量(无const),而且还会动态分配它而不会在某处被释放。另外,它可以释放到任何地方,如果不小心,可能会不止一次。无论哪种方式,由于delete已经进行了自己的内存管理,因此只需省略std::string即可。

假设没有C ++ 11(无法访问new),您应该以这个结尾:

const std::string weekDays[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};


在C ++中,初始化C数组时最好也将std::array留空。编译器将自行确定正确的大小,因此您不必担心自己保持最新状态。

#2 楼

我只关注getMonthValue()


int getMonthValue(const size_t month, const size_t year)
{
    switch (month) {
        case January:
            if (isLeapYear(year)) return 6;
        case October:
            return 0;
        case May:
            return 1;
        …



month和year参数不是大小。当您只是说size_t时,请勿使用unsigned int。但是,您也有bool isLeapYear(const int year)-为什么year类型不一致?此外,您还定义了一个非常好的Months枚举。为什么不使用单数重命名并使用它呢?无论如何,您都隐式地在切换块中比较size_tMonths

我将使用查找表来代替切换块。实际上,有两个查询表:一个用于leap年,一个用于非-年。数据驱动的设计将具有更少的代码。

int getMonthValue(int month, int year) {
    const static int NON_LEAP_VALS[] = { 0xDEAD, 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 },
                         LEAP_VALS[] = { 0xDEAD, 6, 2, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 };
    return (isLeapYear(year)) ? LEAP_VALS[month] : NON_LEAP_VALS[month];
}


评论


\ $ \ begingroup \ $
使用虚拟0xDEAD条目代替使用[month + 1]索引是否是数据驱动设计的实例?
\ $ \ endgroup \ $
–俄罗斯
17年8月28日在16:22

#3 楼

bool isLeapYear(const int year)
{
    if (0 == (year % 400) || (0 == (year % 4) && (year % 100))) return true;
    else return false;
}


这很挑剔,但是我有点不明白。括号的用法不一致使我难以考虑求值顺序。

此外,您使用0 ==表示假值,但不使用0 !=表示真值,我也发现不一致。

if/else也有点多余。

我将其重写为:

bool isLeapYear(const int year)
{
    return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));
}


或如果不使用== 0

bool isLeapYear(const int year)
{
    return !(year % 400) || (!(year % 4) && (year % 100));
}


评论


\ $ \ begingroup \ $
我还认为年份%100的逻辑不正确-如果可以将其除以4而不是100,则它是a年。
\ $ \ endgroup \ $
– GalacticCowboy
14-10-23在17:21

\ $ \ begingroup \ $
@GalacticCowboy年%100与书写年%100!= 0相同,因为任何非零结果都等于true。
\ $ \ endgroup \ $
– Rottem
2014-10-23 17:23



\ $ \ begingroup \ $
是的,是的。我认为他的原始逻辑是有缺陷的。
\ $ \ endgroup \ $
– GalacticCowboy
14-10-23在17:38

\ $ \ begingroup \ $
@GalacticCowboy我不明白为什么。 ==的优先级比&&的优先级高,0 ==(%4年)将在(%100年)之前评估。我想念什么吗?
\ $ \ endgroup \ $
– Rottem
14-10-23在18:04

\ $ \ begingroup \ $
虽然您的内容在语法上是正确的,但使用起来更容易理解:return((year%400)== 0)|| ((((年%4)== 0)&&((年%100)!= 0));
\ $ \ endgroup \ $
–R Sahu
2014年12月8日在3:07



#4 楼

我得到了代码并根据此处的注释进行了改进,我主要对函数isLeapYear进行了一些更改,因为无需在iostream中创建函数就已经使__isleap运行了100%:https://pastebin.com / 7xsM2CSF

#include <limits> //numeric_limits
#include <iostream>
//https://codereview.stackexchange.com/questions/67636/is-it-friday-yet
//dayOfWeek.cpp

enum Months { January=1, February, March, April, May, June, July, August, September, October, November, December};

const std::string weekDays[7]={"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

/**
 * Makes sure data isn't malicious, and signals user to re-enter proper data if invalid
 * getSanitizedNum
 */
int getInt()
{
    int input = 0;
    while(!(std::cin>>input))
    {
        // clear the error flag that was set so that future I/O operations will work correctly
        std::cin.clear();
        // skips to the next newline
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        std::cout << "\n\tInvalid input.\n\tPlease enter a positive number: ";
    }
    return input;
}

/**
 * Safetly grabs and returns a lowercase version of the character (if the lowercase exists)
 * getSanitizedChar
 */
char32_t getChar()
{
    // absorb newline character (if existant) from previous input
    if('\n' == std::cin.peek()) std::cin.ignore();
    return std::tolower(std::cin.get());
}

int getCenturyValue(int year)
{
    return (2 * (3 - div(year / 100, 4).rem));
}

int getYearValue(int year)
{
    int mod = year % 100;
    return (mod + div(mod, 4).quot);
}

int getMonthValue(int month, int year) 
{
    const static int NON_LEAP_VALS[] = { 0xDEAD, 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 },
                         LEAP_VALS[] = { 0xDEAD, 6, 2, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 };

  return (__isleap(year) ? LEAP_VALS[month] : NON_LEAP_VALS[month]);
}
/*
int getMonthValue(int month, int year)
{
    switch (month) 
    {
        case January:
            if (__isleap(year)) return 6;
        case October:
            return 0;
        case May:
            return 1;
        case August:
            return 2;
        case February:
            if (__isleap(year)) return 2;
        case March:
        case November:
            return 3;
        case June:
            return 4;
        case September:
        case December:
            return 5;
        case April:
        case July:
            return 6;
        default:
            return -1;
    }
}
*/
int dayOfWeek(int day, int month, int year)
{
    return div(day + getMonthValue(month, year) + getYearValue(year) + getCenturyValue(year), 7).rem;
}

void getInput(int &day, int &month, int &year)
{
 int meses[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
 do{
    do{
       std::cout << "\n\tEnter the day (1-31): ";
       day = getInt();
      }while(day < 1 || day > 31);

    do{  
       std::cout << "\n\tEnter the month (1-12): ";
       month = getInt();
      }while(month < 1 || month > 12);

    do{  
       std::cout << "\n\tEnter the year: ";
       year = getInt();
      }while(year < 1100);

      if(__isleap(year))meses[1] = 29;

      if(day > meses[month-1])std::cout<<"\n\tO mes "<<month<<" do ano de "<<year<<" nao tem "<<day<<" dias!!!\n\n";

  }while(day > meses[month-1]);
}

int main()
{
 int day = 0;
 int month = 0;
 int year = 0;

 do{
    getInput(day, month, year);
    std::cout << "\n\tThe day of the week is " << weekDays[dayOfWeek(day, month, year)] << std::endl;

    std::cout << "\n\tRun the program again (y/N): ";  // signify n as default with capital letter

   }while('y' == getChar());

  return 0;
}


评论


\ $ \ begingroup \ $
解释您对isLeapYear()所做的更改以及为什么这样做更好会很有用
\ $ \ endgroup \ $
– Billal Begueradj
17年12月23日在13:08

\ $ \ begingroup \ $
如果语言具有特定功能,则不要重新发明轮子。
\ $ \ endgroup \ $
– dark777
17年12月23日在14:03