Search code examples
smlsmlnj

What is wrong with my function


i have the following function that is supposed to return true if the passed argument is a reasonable date and false otherwise. the problem is that it is returning false even for obviously reasonable dates and i can't figure out what is wrong with it. anyone with sharper eyes please help. here it is:

fun reasonable_date(x: int*int*int) =
    if #1 x > 0 andalso #2 x > 0 andalso #2 x <= 12 andalso #3 x > 0 andalso #3 x <= 31 
    then                                
    if #2 x = 1 mod 2 andalso #2 x < 8 andalso #3 x <= 31 then true
         else if #2 x = 0 mod 2 andalso #2 x >= 8 andalso #3 x <= 31 
         then true
     else if #2 x = 0 mod 2 andalso #2 x < 8
     then
         if #2 x = 2 andalso (#3 x = 28 orelse #3 x = 29) then true
         else if #2 x = 0 mod 2 andalso #3 x <= 30 then true
         else false
         else if #2 x = 1 mod 2 andalso #2 x > 8 andalso #3 x <=30 then true 
     else false
     else false

Solution

  • Your current solution is impossible to maintain, and its logic looks like something that has been to hell and back :)

    I would recommend that you break it up into smaller logical parts that ensure simple properties. Thus instead of first testing whether the year, month and day is greater or equal to one, you could group all the logic regarding years, months and days for itself

    fun daysInMonth n =
        List.nth([31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], n-1)
    
    fun reasonable_date (y, m, d) =
        (* Check year >= 1 *)
        y >= 1 andalso
    
        (* Check 1 <= month <= 12 *)
        (m >= 1 andalso m <= 12) andalso
    
        (* Check 1 <= day <= n, for n being the number of days in specified month *)
        (d >= 1 andalso d <= daysInMonth m)
    

    Obviously this doesn't handle leap years, however that is also quite simple to implement using a helper function, if the month is February. It could be done like this

    fun reasonable_date (y, m, d) =
        (* Check year >= 1 *)
        y >= 1 andalso
    
        (* Check 1 <= month <= 12 *)
        (m >= 1 andalso m <= 12) andalso
    
        (* Check 1 <= day <= n, for n being the number of days in specified month *)
        (d >= 1 andalso
         (* If February, and leap year *)
         ((m = 2 andalso isLeapYear y andalso d <= 29)
          (* Any other month or non leap year *)
          orelse d <= daysInMonth m))