[erlang-questions] Pattern Matching Question

Håkan Stenholm hokan.stenholm@REDACTED
Fri Nov 23 23:13:59 CET 2007


Esbjörn Dominique wrote:
> hi Justin,
>
> My advice would be to turn your days_in to a helper function, e.g.
> days_in1, something like:
This could be simplified to the following, especially if we can trust 
the input values to be valid:


-module(date).

-export([days_in_month/2]).

%% feb is the only/special case that needs to check for leap year
days_in_month(feb, Y) ->  
    case is_leap(Y) of
        true -> 29;
        false -> 28
    end;

days_in_month(M, _) ->  
    %% use of case - reduces code boilderplate
    case M of
       jan -> 31;
       mar -> 31;
       may -> 31;
       jul -> 31;
       aug -> 31;
       oct -> 31;
       dec -> 31;
       _ -> 30 % apr, jun, sep, nov
    end.

is_leap(Year) -> % if reduces need for boilderplate
	if 
		
		%% implicit is_integer/1 test, rem assumes a int()
		(Year rem 400) == 0 -> true;
		(Year rem 100) == 0 -> false;
		(Year rem   4) == 0 -> true;
		
		%% wont get here if Year isn't a int()
		true  -> false                 
	end.



>
> days_in1(jan, _) -> 31;
> days_in1(feb, common) -> 28;
> days_in1(feb, leap) -> 29;
> days_in1(mar, _) -> 31;
> days_in1(apr, _) -> 30;
> days_in1(may, _) -> 31;
> days_in1(jun, _) -> 30;
> days_in1(jul, _) -> 31;
> days_in1(aug, _) -> 31;
> days_in1(sep, _) -> 30;
> days_in1(oct, _) -> 31;
> days_in1(nov, _) -> 30;
> days_in1(dec, _) -> 31.
>
> days_in(Month, common) ->
>     days_in1(Month, common);
> days_in(Month, leap) ->
>     days_in1(Month, leap);
> days_in(Month, Year) when is_integer(Year) ->
>     days_in1(Month, leap(Year)).
>
> leap(Year) when Year rem 400 == 0 -> leap   ;
> leap(Year) when Year rem 100 == 0 -> common ;
> leap(Year) when Year rem   4 == 0 -> leap   ;
> leap(Year) when integer(Year)     -> common .
>
> And only export days_in/2, ofcourse.
>
> What do you think?
> /esbjörn
>
>
> On Nov 23, 2007 8:45 PM, Justin Piper <justin.piper@REDACTED> wrote:
>> I'm working through an introductory tutorial on Erlang, and one of the
>> exercises asked for a function that returns the number of days in a
>> given month, which I implemented this way:
>>
>>    days_in(jan, _     ) ->          31 ;
>>    days_in(feb, common) -> 28          ;
>>    days_in(feb, leap  ) ->    29       ;
>>    days_in(mar, _     ) ->          31 ;
>>    days_in(apr, _     ) ->       30    ;
>>    days_in(may, _     ) ->          31 ;
>>    days_in(jun, _     ) ->       30    ;
>>    days_in(jul, _     ) ->          31 ;
>>    days_in(aug, _     ) ->          31 ;
>>    days_in(sep, _     ) ->       30    ;
>>    days_in(oct, _     ) ->          31 ;
>>    days_in(nov, _     ) ->       30    ;
>>    days_in(dec, _     ) ->          31 ;
>>
>>    days_in(Month, Year)
>>    -> days_in(Month, leap(Year)) .
>>
>>    leap(Year) when Year rem 400 == 0 -> leap   ;
>>    leap(Year) when Year rem 100 == 0 -> common ;
>>    leap(Year) when Year rem   4 == 0 -> leap   ;
>>    leap(Year) when integer(Year)     -> common .
>>
>> You can either explicitly tell days_in/2 if it is a leap year using
>> 'common' and 'leap', or you can give it an integer and it will
>> determine whether it is a leap year.
>>
>> This works, but as you can see, since February is the only month that
>> is actually different during a leap year, it's the only one that cares
>> what you pass in the second argument. Since passing something other
>> than 'leap', 'common' or an integer is surely an error, it'd be nice
>> if this were indicated for all of the months. Does Erlang provide an
>> elegant way to do this? I know I could just have two clauses for each
>> month:
>>
>>    days_in(jan, common) -> 31 ;
>>    days_in(jan, leap  ) -> 31 ;
>>
>> But that seems like needless duplication. I also know I could rewrite
>> days_in/2 to have just one clause for all twelve months and just use
>> case to select the correct number of days:
>>
>>    days_in(Month, Year)
>>    when Year == common; Year == leap
>>    -> case { Month, Year }
>>       of { jan, _     } ->          31 ;
>>          { feb, common} -> 28          ;
>>          { feb, leap  } ->    29       ;
>>          { mar, _     } ->          31 ;
>>          { apr, _     } ->       30    ;
>>          { may, _     } ->          31 ;
>>          { jun, _     } ->       30    ;
>>          { jul, _     } ->          31 ;
>>          { aug, _     } ->          31 ;
>>          { sep, _     } ->       30    ;
>>          { oct, _     } ->          31 ;
>>          { nov, _     } ->       30    ;
>>          { dec, _     } ->          31
>>       end;
>>
>> But I think that the version that uses a clause per month is a little
>> easier to read, particularly if you're just scanning the code. I was
>> hoping Erlang would have an easy way to say that the second argument
>> has to be either 'leap', 'common' or an integer, and that anything
>> else is an error.
>> _______________________________________________
>> erlang-questions mailing list
>> erlang-questions@REDACTED
>> http://www.erlang.org/mailman/listinfo/erlang-questions
>>
>
>
>



More information about the erlang-questions mailing list