[erlang-questions] Pattern Matching Question
Esbjörn Dominique
esbjorn@REDACTED
Sat Nov 24 08:47:40 CET 2007
This reminds me of something that have bothered me some times (well,
not that much): the lack of fall-through á la C switch/case. What I
would like is something like:
days_in(jan, _);
days_in(mar, _);
days_in(may, _);
days_in(jul, _);
days_in(aug, _);
days_in(oct, _) -> 31;
days_in(dec, _);
days_in(apr, _);
days_in(jun, _);
days_in(sep, _);
days_in(nov, _) -> 30;
days_in(feb, common) -> 28;
days_in(feb, leap) -> 29.
The motivation is simply to reduce duplication.
It would just be a syntacic sugar - I guess the compiler should treat
it as if the function body is duplicated in all fall-through cases.
No doubt this could be mis-used and messy, but I think it could be useful.
It could also apply to e.g. case:
days_in_month(M, _) ->
case M of
jan;
mar;
may;
jul;
aug;
oct;
dec -> 31;
_ -> 30 % apr, jun, sep, nov
end.
Any opinions?
/esbjörn
On Nov 23, 2007 11:13 PM, Håkan Stenholm <hokan.stenholm@REDACTED> wrote:
> 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
> >>
> >
> >
> >
> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://www.erlang.org/mailman/listinfo/erlang-questions
>
More information about the erlang-questions
mailing list