views:

303

answers:

9

I have this insane homework where I have to create an expression to validate date with respect to Julian and Gregorian calendar and many other things ...

The problem is that it must be all in one expression, so I can't use any ;

Are there any options of defining variable in expression? Something like

d < 31 && (bool leapyear = y % 4 == 0) || (leapyear ? d % 2 : 3) ....

where I could define and initialize one or more variables and use them in that one expression without using any ;?

Edit: It is explicitly said, that it must be a one-line expression. No functions ..

The way I'm doing it right now is writing macros and expanding them, so I end up with stuff like this

#define isJulian(d, m, y) (y < 1751 || (y == 1752 && (m < 9) || (m == 9 && d <= 2)))
#define isJulianLoopYear(y) (y % 4 == 0)
#define isGregorian(d, m, y) (y > 1573 || (y == 1752 && (m > 9) || (m == 9 && d > 13)))
#define isGregorianLoopYear(y) ((y % 4 == 0) || (y % 400 = 0))
// etc etc ....

looks like this is the only suitable way to solve the problem

edit: Here is original question

Suppose we have variables d m and y containing day, month and year. Task is to write one single expression which decides, if date is valid or not. Value should be true (non-zero value) if date is valid and false (zero) if date is not valid.

This is an example of expression (correct expression would look something like this):

d + 4 == y ^ 85 ? ~m : d * (y-2)

These are examples of wrong answers (not expressions):

if ( log ( d ) == 1752 ) m = 1;

or:

for ( i = 0; i < 32; i ++ ) m = m / 2;

Submit only file containing only one single expression without ; at the end. Don't submit commands or whole program.

  • Until 2.9.1752 was Julian calendar, after that date is Gregorian calendar
  • In Julian calendar is every year dividable by 4 a leap year.
  • In Gregorian calendar is leap year ever year, that is dividible by 4, but is not dividible by 100. Years that are dividable by 400 are another exception and are leap years.
  • 1800, 1801, 1802, 1803, 1805, 1806, ....,1899, 1900, 1901, ... ,2100, ..., 2200 are not loop years.
  • 1896, 1904, 1908, ..., 1996, 2000, 2004, ..., 2396,..., 2396, 2400 are loop years
  • In september 1752 is another exception, when 2.9.1752 was followed by 14.9.1752, so dates 3.9.1752, 4.9.1752, ..., 13.9.1752 are not valid.
+2  A: 

In standard C++, this is not possible. G++ has an extension known as statement expressions that can do that.

Martin v. Löwis
+2  A: 

I don't believe you can, but even if you could, it would only have scope inside of the parentheses they are defined in (in your example) and cannot be used outside of them.

Jim Buck
+3  A: 

I think the intent of the homework is to ask you to do this without using variables, and what you are trying to do might defeat its purpose.

Ashwin
May-be the intent is to use a suitable library?
UncleBens
If that's the intent, then the assignment should have asked for that.
Chuck
A: 

You clearly have to have the date passed in somehow. Beyond that, all you're really going to be doing is chaining && and || (assuming we get the date as a tm struct):

#include <ctime>
bool validate(tm date)
{
     return (
             // sanity check that all values are positive
             date.tm_mday >= 1 && date.tm_mon >= 0 && date.tm_year >= 0
             // check for valid days
             && ((date.tm_mon == 0 && date.tm_mday <= 31)
              || (date.tm_mon == 1 && date.tm_mday <= (date.tm_year % 4 ? 28 : 29))
              || (date.tm_mon == 2 && date.tm_mday <= 31)
             // yadda yadda for the other months
              || (date.tm_mon == 11 && date.tm_mday <= 31))
             );
}

The parenthesis around date.tm_year % 4 ? 28 : 29 actually aren't needed, but I'm including them for readability.


UPDATE

Looking at a comment, you'll also need similar rules to validate dates that don't exist in the Gregorian calendar.

UPDATE II

Since you're dealing with dates in the past you will need to implement a more correct leap year test. However, I generally deal with dates in the future, and this incorrect leap year test will give correct results in 2012, 2016, 2020, 2024, 2028, 2032, 2036, 2040, 2044, 2048, 2052, 2056, 2060, 2064, 2068, 2072, 2076, 2080, 2084, 2088, 2092, and 2096. I will make a prediction that before this test fails in 2100 silicon-based computers will be forgotten relics. I seriously doubt we will use C++ on the quantum computers in use then. Besides, I won't be the programmer assigned to fix the bug.

Max Lybbert
29/2/1900 => fail, nonexistent date :-P
Chris Jester-Young
See http://www.joelonsoftware.com/items/2006/06/16.html for one area where this fail has had lasting consequences, to this day. (Go Lotus!)
Chris Jester-Young
+1  A: 

First: Don't. It may be cute, but even if there's an extension that allows it, code golf is a dangerous game that will almost always end up causing mmore grief than it solves.

Okay, back to the 'real' question as defined by the homework. Can you make additional functions? If so, instead of capturing whether or not it's a leap year in a variable, make a function isLeapYear(int year) that returns the correct value.

Yes, that means you'll calculate it more than once. If that ends up being a performance issue, I'd be incredibly surprised... and it's a premature optimization to worry about that in the first place.

I'd be very surprised if you weren't allowed to write functions as part of doing this. It seems like that'd be half the point of an exercise like this.

......

Okay, so here's a quick overview of what you'll need to do.

First, basic verification - that month, day, year are possible values at all - month 0-11 (assuming 0-based), day 0-30, year non-negative (assuming that's a constraint).

Once you're past that, I'd probably check for the 1752 special cases.

If that's not relevant, the 'regular' months can be handled pretty simply.

This leaves us with the leap year cases, which can be broken down into two expressions - whether something is a leap year (which will be broken down additionally based on gregorian/julian), and whether the date is valid at that point.

So, at the highest level, your expression looks something like this:

areWithinRange(d,m,y) && passes1752SpecialCases(d,m,y) && passes30DayMonths(d,m,y) && passes31DayMonths(d,m,y) && passesFebruaryChecks(d,m,y)

If we assume that we only return false from our sub-expressions if we actively detect a rule break (31 days in June for the 30DayMonth rule returns false, but 30 days in February is irrelevant and so passes true), then we can pretty much say that the logic at that level is correct.

At this point, I'd write separate functions for the individual pieces (as pure expressions, a single return ... statement). Once you've gotten those in place, you can replace the method call in your top-level expression with the expanded version. Just make sure you parenthesize (is that a word?) everything sufficiently.

I'd also make a test harness program that uses the expression and has a number of valid and invalid inputs, and verifies that you're doing the right thing. You can write that in a function for ease of cut and paste for the final turn-in by doing something like:

bool isValidDate(int d, int m, int y)
{
    return
        // your expression here
}

Since the expression will be on a line by itself, it'll be easy to cut and paste.

You may find other ways to simplify your logic - excepting the 1752 special cases, days between 1 and 28 are always valid, for instance.

kyoryu
Actually I can't write any functions. It is explicitly said that I can submit only a one-line expression
Darth
That's surprising, but I'd guess it's to prevent 'cheating' by hiding state in function access. Still, the macro solution is likely to be considered equivalent to functions. I think you're just going to have to use the ?: operator a lot, and accept that there may be some redundant code. Learning to do stuff like this as an expression is a good exercise, as there's no reason to add state to a 'pure function' like this except convenience. What are the expected inputs and outputs of this experssion?
kyoryu
@kyoryu: I'm using macros only to make code readable, since it ends up in lots of ?: operators, expected input is three "variables" d m y and output should be true/false
Darth
It's your assignment :) I'm just suggesting that if the teacher doesn't want isJulian(), they probably don't care if it's a macro or a function. Again - what are the expected inputs and outputs? I might be able to help more. The actual text of the question may be useful as well.
kyoryu
@kyoryu: see edit for complete original question
Darth
+1  A: 

Your solution, which I will not provide fully for you, will probably go along these lines:

isJulian ? isJulianLeapyear : isGregorianLeapyear

To make it more specific, it could be like this:

isJulian ? (year % 4) == 0 : ((year % 4) == 0 || (year % 400) == 0)

You'll have to just make sure your algorithm is correct. I'm not a calender expert, so I wouldn't know about that.

ZachS
A: 

Considering it is homework, I think the best advice would be an approach to deriving your own solution.

If I were tackling this assignment, I would start by breaking the rules.

  1. I would write a c++ function that given the variables d, m, and y, returns a boolean result on the validity of the date. I would use as many non-recursive helper functions as needed, and feel free to use if, else if, and else, but no looping aloud.

  2. I would then Inline all helper functions

  3. I would reduce all if, else if, and else statement to ? : notation

If I was successful at limiting my use of variables, I might be able to reduce all this to a single function with no variables - the body of which will contain the single expression I seek.

Good Luck.

csj
+5  A: 
((m >0)&&(m<13)&&(d>0)&&(d<32)&&(y!=0)&&(((d==31)&&
((m==1)||(m==3)||(m==5)||(m==7)||(m==8)||(m==10)||(m==12)))
||((d<31)&&((m!=2)||(d<29)))||((d==29)&&(m==2)&&((y<=1752)?((y%4)==0):
((((y%4)==0)&&((y%100)!=0))
||((y%400)==0)))))&&(((y==1752)&&(m==9))?((d<3)||(d>13)):true))
polrinvammy
+2  A: 

<evil> Why would you define a new one, if you can reuse an existing one? errno is a perfect temporary variable. </evil>

MSalters