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