Без комментария к алгоритму, или на использовании соответствующих библиотечных функций...
Я ожидал бы видеть больше использования сопоставления с образцом и рекурсии; например, parse_character (больше не свернутый) мог бы быть заменен чем-то как:
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]).
Начатый с a
parse_in_format (FormatStr, [], [], "");
В дополнение к предложению doug я избегал бы использования atom_to_list/1
здесь - для кода имен замены не нужны они, и генерирующиеся атомы во времени выполнения почти всегда плохая идея. Строки будут работать отлично.
parse_name([$}|FS],FormatString,ParamList,ParamName) ->
parse_format(FS,FormatString,[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]).
Я также использовал бы proplists:get_value вместо lists:keysearch/3
- когда у Вас есть список двух кортежей элемента {Name, Value}
поскольку мы делаем здесь, с помощью proplists
код является способом пойти - это все еще немного грязно, поскольку нам нужен оператор выбора для проверки на отсутствующие значения, таким образом, мы можем отказать с лучшей ошибкой.
substitute_names(Positioned,Values) ->
[ case proplists:get_value(Name, Values) of
undefined -> erlang:exit({missing_parameter, Name});
V -> V
end
|| Name <- Positioned ].
Поскольку это - библиотека, это должна быть замена для io_lib
, нет io
. Таким образом, мы не должны обеспечивать все альтернативы io
предложения (дополнительный IoDevice
аргумент и так далее).
format(OString,ONames) ->
{FString,FNames}=parse_string(OString,ONames),
io_lib:format(FString,FNames).
В целом, основательный код. Если бы Вы готовы лицензировать его под BSD или чем-то подобным, я вполне хотел бы добавить, что это к моей веб-платформе кодирует Ejango.
Если Вы не знаете, влияет ли цикличное выполнение наверху на Ваш код очень, необходимо измерить его. Это просто.
-define(COLOOPS, 1000000).
-export([call_overhead/1,measure_call_overhead/0, measure_call_overhead/1]).
% returns overhead in us
measure_call_overhead() -> measure_call_overhead(?COLOOPS).
measure_call_overhead(N) -> element(1, timer:tc(?MODULE, call_overhead, [N]))/N.
call_overhead(0)->ok;
call_overhead(N)->
ok=nop(),
call_overhead(N-1).
nop()->ok.
Это - приблизительно 50 нс на моем ноутбуке. Я думаю, что это не должно влиять на Ваш текущий код так.
Иначе то, как иметь размеры, использует непосредственно статистику (wall_clock) или статистику (время выполнения), которое возвращает время в мс. Преимущество - то, что Вы не должны экспортировать измеряемую функцию. Это - только улучшение косметики.