Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Printing named parameters

Tags:

erlang

Archaelus suggested in this post that writing a new format routine to handle named parameters may be a good learning exercise. So, in the spirit of learning the language I wrote a formatting routine which handles named parameters.



An Example:

1> fout:format("hello ~s{name}, ~p{one}, ~p{two}, ~p{three}~n",[{one,1},{three,3},{name,"Mike"},{two,2}]).
hello Mike, 1, 2, 3
ok



The Benchmark:

1> timer:tc(fout,benchmark_format_overhead,["hello ~s{name}, ~p{one}, ~p{two}, ~p{three}~n",[{one,1},{name,"Mike"},{three,3},{two,2}],100000]).
{421000,true}
= 4.21us per call

Although I suspect that much of this overhead is due to looping, as a calling the function with one loop yields a response in < 1us.

1> timer:tc(fout,benchmark_format_overhead,["hello ~s{name}, ~p{one}, ~p{two}, ~p{three}~n",[{one,1},{name,"Mike"},{three,3},{two,2}],1]).
{1,true}

If there is a better way of benchmarking in erlang, please let me know.



The Code: (which has been revised in accordance with Doug's suggestion)

-module(fout).

-export([format/2,benchmark_format_overhead/3]).

benchmark_format_overhead(_,_,0)->
    true;
benchmark_format_overhead(OString,OList,Loops) ->
    {FString,FNames}=parse_string(OString,ONames),
    benchmark_format_overhead(OString,OList,Loops-1).

format(OString,ONames) ->
    {FString,FNames}=parse_string(OString,ONames),
    io:format(FString,FNames).

parse_string(FormatString,Names) ->
    {F,N}=parse_format(FormatString),
    {F,substitute_names(N,Names)}.

parse_format(FS) ->
    parse_format(FS,"",[],"").

parse_format("",FormatString,ParamList,"")->
    {lists:reverse(FormatString),lists:reverse(ParamList)};
parse_format([${|FS],FormatString,ParamList,"")->
    parse_name(FS,FormatString,ParamList,"");
parse_format([$}|_FS],FormatString,_,_) ->
    throw({'unmatched } found',lists:reverse(FormatString)});
parse_format([C|FS],FormatString,ParamList,"") ->
    parse_format(FS,[C|FormatString],ParamList,"").

parse_name([$}|FS],FormatString,ParamList,ParamName) ->
    parse_format(FS,FormatString,[list_to_atom(lists:reverse(ParamName))|ParamList],"");
parse_name([${|_FS],FormatString,_,_) ->
    throw({'additional { found',lists:reverse(FormatString)});
parse_name([C|FS],FormatString,ParamList,ParamName) ->
    parse_name(FS,FormatString,ParamList,[C|ParamName]).

substitute_names(Positioned,Values) ->
    lists:map(fun(CN)->
                        case lists:keysearch(CN,1,Values) of
                            false ->
                                throw({'named parameter not found',CN,Values});
                            {_,{_,V}} ->
                                V
                        end end,
              Positioned).

As this was a learning exercise, I was hoping that those more experienced with erlang could give me tips on how to improve my code.

Cheers, Mike

like image 593
Mike Hamer Avatar asked Jan 04 '09 04:01

Mike Hamer


1 Answers

Without comment on the algorithm, or on use of appropriate library functions...

I would have expected to see more use of pattern matching and recursion; for example parse_character (no longer folded) might be replaced with something like:

parse_in_format ([], FmtStr, ParmStrs, ParmName) -> {FmtStr, ParmStrs};
parse_in_format ([${ | Vr], FmtStr, ParmStrs, ParmName) -> parse_in_name (Vr, FmtStr, ParmStrs, ParmName);
parse_in_format ([$} | Vr], FmtStr, ParmStrs, ParmName) -> throw() % etc.
parse_in_format ([V | Vr], FmtStr, ParmStrs, ParmName) -> parse_in_format (Vr, [V | FmtStr], ParmStrs, ParmName).

parse_in_name ([], FmtStr, ParmStrs, ParmName) -> throw() % etc.
parse_in_name ([$} | Vr], FmtStr, ParmStrs, ParmName) -> parse_in_format (Vr, FmtStr, [list_to_atom(lists:reverse(ParmName))|ParmStrs], "");
parse_in_name ([${ | Vr], FmtStr, ParmStrs, ParmName) -> throw() % etc.
parse_in_name ([V | Vr], FmtStr, ParmStrs, ParmName) -> parse_in_name (Vr, FmtStr, ParmStrs, [V | ParmName]).

Kicked off with a

parse_in_format (FormatStr,  [], [], "");
like image 109
Doug Currie Avatar answered Nov 14 '22 23:11

Doug Currie