Python Forum
Tracking leap.py years for gregorian calendar (Exercism org)
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Tracking leap.py years for gregorian calendar (Exercism org)
#1
This is not for a course credit. It's just online courseware. Although I'd prefer that you people not provide a full solution. Instead just provide guidance, hints, and support in a general direction. Thanks.

Here are the Instructions:

Quote: Given a year, report if it is a leap year.
The tricky thing here is that a leap year in the Gregorian calendar occurs:

Quote:on every year that is evenly divisible by 4
except every year that is evenly divisible by 100
unless the year is also evenly divisible by 400

For example, 1997 is not a leap year, but 1996 is. 1900 is not a leap year, but 2000 is.
For a delightful, four minute explanation of the whole leap year phenomenon, go watch this youtube video.

There is only one variable - - a given year - - but so many different possible cases and conditions that the unit test tries to verify.

The problem involves basic predicate logic and bivalence. I’ve tried many different combinations of operators, like swapping the three instances of disjunctions and conjunctions, back and forth, one for the other. Every time I change one of the terms, I run the test script, and as a result, a different set of Assertions pass while others fail. I’ve also tried different combinations of equality and inequality operators which will also trigger different Assertions to pass and fail. After each change, I am no closer to achieving the end goal which is to have all of them pass.

For the return line, if I change True for False, the Assertions that previously failed, are now passing, which kind of makes sense.

I’ve done my very best to use the exercise’s description as pseudo code and guide:

Quote:on every year that is evenly divisible by 4
except every year that is evenly divisible by 100
unless the year is also evenly divisible by 400

But I am missing something and not sure what.

Here is my script:

def leap_year(year):
   if (year % 4 == 0 and year % 2 == 0) and (year % 100 == 0) or (year % 400 != 0): # or (year % 100 != 3):
       return True
Using that script, here is my unit test trace back passing 4 tests and failing 5 tests:

Quote: ============ short test summary info ============
FAILED python/leap/leap_test.py::LeapTest::test_year_divisible_by_100_but_not_by_3_is_still_not_a_leap_year - AssertionError: True is not False
FAILED python/leap/leap_test.py::LeapTest::test_year_divisible_by_100_not_divisible_by_400_in_common_year - AssertionError: True is not False
FAILED python/leap/leap_test.py::LeapTest::test_year_divisible_by_200_not_divisible_by_400_in_common_year - AssertionError: True is not False
FAILED python/leap/leap_test.py::LeapTest::test_year_divisible_by_2_not_divisible_by_4_in_common_year - AssertionError: True is not False
FAILED python/leap/leap_test.py::LeapTest::test_year_not_divisible_by_4_in_common_year - AssertionError: True is not False
========== 5 failed, 4 passed in 0.07s ==========

Here is the unit test class revealing the test conditions:

class LeapTest(unittest.TestCase):
   def test_year_not_divisible_by_4_in_common_year(self):
       self.assertIs(leap_year(2015), False)
 
   def test_year_divisible_by_2_not_divisible_by_4_in_common_year(self):
       self.assertIs(leap_year(1970), False)
 
   def test_year_divisible_by_4_not_divisible_by_100_in_leap_year(self):
       self.assertIs(leap_year(1996), True)
 
   def test_year_divisible_by_4_and_5_is_still_a_leap_year(self):
       self.assertIs(leap_year(1960), True)
 
   def test_year_divisible_by_100_not_divisible_by_400_in_common_year(self):
       self.assertIs(leap_year(2100), False)
 
   def test_year_divisible_by_100_but_not_by_3_is_still_not_a_leap_year(self):
       self.assertIs(leap_year(1900), False)
 
   def test_year_divisible_by_400_is_leap_year(self):
       self.assertIs(leap_year(2000), True)
 
   def test_year_divisible_by_400_but_not_by_125_is_still_a_leap_year(self):
       self.assertIs(leap_year(2400), True)
 
   def test_year_divisible_by_200_not_divisible_by_400_in_common_year(self):
       self.assertIs(leap_year(1800), False)
Reply
#2
I don't see this requirement stated in the problem.
year % 2 == 0
It doesn't matter, anything divisible by 4 is divisible by 2, but it shouldn't be there.

One of your restrictions is wrong.

All leap years are divisible by 4.
Some years divisible by 4 are not leap years. If a year is divisible by 100 it is not a leap year UNLESS it is divisible by 400.
Reply
#3
(Apr-02-2022, 08:07 PM)deanhystad Wrote: I don't see this requirement stated in the problem.
year % 2 == 0

There is one line in the unit test which tests years like 1970 which are divisible by 2 but not 4:

    def test_year_divisible_by_2_not_divisible_by_4_in_common_year(self):
        self.assertIs(leap_year(1970), False)
I tried to account for that in my original script.

Maybe I misunderstood that particular requirement.

I nuked my approach from orbit and started over. This script I came up with passes all the unit tests:

def leap_year(year):
    if (year % 400 == 0) and (year % 4==0):
        return True
    if (year % 4 == 0) and (year % 100 != 0):
        return True
    elif (year % 100!=0) and (year % 4 != 0):
        return False  
    else:
        return False
Now that I resolved this exercise on my own in the above crude and unpythonic fashion, I am open and receptive to hearing from other veteran members and professional developers of this forum to share their alternate whiz-bang solutions with advanced one-liners.
Reply
#4
I think that solution can be reduced to using and and or. From documentation (Boolean operations)

Quote:The expression x and y first evaluates x; if x is false, its value is returned; otherwise, y is evaluated and the resulting value is returned.

The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.

def leap_year(year):
    return year % 4 == 0 and year % 100 != 0 or year % 400 == 0
I'm not 'in'-sane. Indeed, I am so far 'out' of sane that you appear a tiny blip on the distant coast of sanity. Bucky Katt, Get Fuzzy

Da Bishop: There's a dead bishop on the landing. I don't know who keeps bringing them in here. ....but society is to blame.
Reply
#5
Do you know Nassi–Shneiderman diagrams? It is a way to draw an algorithm, apart from the language in which you want to implement the algorithm. I like them because they show visually what the algorithm does.

[Image: attachment.php?aid=1690]

The upside-down triangles are choices; so IF statements in Python. Under that there are blocks denoting what has to be done in case the result of the choice is True or False.
Reply
#6
This is the elegant one-liner I knew someone would come up with:
(Apr-03-2022, 08:07 AM)perfringo Wrote:
def leap_year(year): 
     return year % 4 == 0 and year % 100 != 0 or year % 400 == 0  

Thank you for sharing this, perfringo. It is instructive.

(Apr-03-2022, 08:07 AM)perfringo Wrote: I think that solution can be reduced to using and and or. From documentation (Boolean operations)

Referring to the Boolean operations in the official Python docs is redundant. I was well aware. If you go back and re-read my original thread, you'll see that I already referred to "predicate logic", "bivalence", "dysjunctions" and "conjunctions". Boolean operations are the same thing. You say "potato" I say "potatoe". I guess perhaps I just need to come to terms with the fact that not all Python developers have studied Formal Logic like Irving M Copi, Russell Marcus, et. al.

Quote:Do you know Nassi–Shneiderman diagrams? It is a way to draw an algorithm, apart from the language in which you want to implement the algorithm. I like them because they show visually what the algorithm does.

Nassi–Shneiderman diagrams are the first I've heard of them. That diagram is easy to follow along and makes sense. I am curious, ibreeden: How did you interpolate and customize your diagram to match the specifics of this forum thread involving leap years and different conditions? Or did you find that leap_year diagram pre-made eleswhere on the web?

Also: Are those walrus operators introduced in Python v3.8?
Reply
#7
Walrus operators added in 3.8. Makes me feel like I am back writing Pascal.
Reply
#8
(Apr-03-2022, 02:50 PM)Drone4four Wrote: How did you interpolate and customize your diagram to match the specifics of this forum thread involving leap years and different conditions?
I use "Structorizer" to make Nassi-Shneiderman diagrams. "Structorizer" is created by https://structorizer.fisch.lu/ and as the latest version is 3.32-06 (2022-01-04), it appears it is activlely maintained. You can download the app for Linux, Windows and Mac.

And about the walrus operators: I use them in NS diagrams because it is the assignment operator in structured programming and is understood as such by "Structorizer".
Reply
#9
(Apr-03-2022, 02:50 PM)Drone4four Wrote: Referring to the Boolean operations in the official Python docs is redundant. I was well aware. If you go back and re-read my original thread, you'll see that I already referred to "predicate logic", "bivalence", "dysjunctions" and "conjunctions". Boolean operations are the same thing. You say "potato" I say "potatoe". I guess perhaps I just need to come to terms with the fact that not all Python developers have studied Formal Logic like Irving M Copi, Russell Marcus, et. al.

For starters I would like to clarify that (a) I don't speak English natively (b) I am not Python developer.

Despite that I would like to point out some things. My quote to documentation was about how this is implemented in Python.

In Java (and in many other languages) && (logical and) and ! (logical not) always require operands to be booleans (or evaluate to booleans) and return boolean as well.

In Python expressions on both sides are not limited to evaluate to boolean, furthermore and and not behave differently: in case of and result of last expression evaluated is returned (it can be True, False, 'spam' or whatnot) and in case in not boolean value is returned.

I believe that knowing formal logic is not enough in this situation - one must know how to take advantage of implementation of boolean operations/operators in specific programming language. In current case it didn't make difference but knowing it is important.

>>> 4 and 2
2


In Java 4 && 2 will raise error: bad operand types for binary operator '&&' as it expects booleans instead of integers.

Another example how in Python boolean operations can be used:

>>> nums = [1, 3, 6, 7, 8]
>>> for num in nums:
...     print(f"{num} is {num % 2 == 0 and 'even' or 'odd'}")
...
1 is odd
3 is odd
6 is even
7 is odd
8 is even
... and yes, I haven't studied Formal Logic like Irving M Copi, Russell Marcus, et. al.
I'm not 'in'-sane. Indeed, I am so far 'out' of sane that you appear a tiny blip on the distant coast of sanity. Bucky Katt, Get Fuzzy

Da Bishop: There's a dead bishop on the landing. I don't know who keeps bringing them in here. ....but society is to blame.
Reply
#10
Quote:There is one line in the unit test which tests years like 1970 which are divisible by 2 but not 4:
def test_year_divisible_by_2_not_divisible_by_4_in_common_year(self):
self.assertIs(leap_year(1970), False)
I tried to account for that in my original script.
If you are designing your code to pass tests instead of designing you code to be correct, you have corrupted the testing process so badly that it is more bane than boon. A test is meant to verify your code, not direct your design. You should not care that there is a test for 1970 unless your code fails that test. You should not even look at the tests unless you don't understand the requirements. Normally you can't look at the tests because they are written during or after coding the program.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
Question Frog codility leap sort variant MoreMoney 5 365 Apr-06-2024, 08:47 PM
Last Post: deanhystad
  Task calendar problem humanical 8 1,676 Sep-04-2023, 02:55 PM
Last Post: Pedroski55
  Calculate AGE in years Kessie1971 17 3,453 Apr-26-2023, 03:36 PM
Last Post: deanhystad
  Exercism with Python Gyga_Hawk 8 118,769 Mar-18-2022, 08:53 AM
Last Post: ndc85430
  Meltdown Mitigation (operators + conditionals) beware: Exercism potential spoiler Drone4four 5 2,623 Feb-21-2022, 08:49 AM
Last Post: Gribouillis
  Calendar program louienyy 2 4,955 Mar-30-2020, 01:21 PM
Last Post: louienyy
Sad [Learning] Calendar without modules or list KoFu 5 4,818 Sep-09-2019, 03:25 PM
Last Post: DeaD_EyE
  Problem with code to calculate weekday for leap years! Event 2 2,840 Dec-15-2018, 05:13 PM
Last Post: Event
  Calendar calculations frequency 10 5,589 Nov-13-2018, 07:34 PM
Last Post: frequency
  python age calculator need to find the number of years before they turn 100 not using orangevalley 4 9,938 Mar-26-2018, 04:44 AM
Last Post: PyMan

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020