[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