Мой вариант функции get_host_from_url, написанной на erlang
Я совсем новичок в функциональном программирование, поэтому вполне возможно склонен к изобретению велосипедов. В связи с чем хочу поделиться своими мыслями по написанию простейшей функции, которая извлекает из урла имя хоста. Очень интересно послушать конструктивную критику, в выражениях можно не стесняться :-).
Итак, начнем с формализации поставленной задачи.
Во-первых, при написании функции будем исходить из того, что на вход всегда подается урл, заданный в виде строки. Пусть erlang сам вываливается с исключительной ситуацией, когда на вход попадает какой-нибудь другой примитив, например массив.
Во-вторых, будем исходить из того, что если на вход поступила строка, то это обязательно валидный урл, а не что-нибудь иное. Поэтому проверку на корректность урла делать так же не будем.
Теперь о том, что мы будем делать. Вот небольшая спецификация того, как должна работать функция:
get_host_from_url("http://www.techlabs.kz") // ["www.techlabs.kz"]
get_host_from_url("http://www.techlabs.kz/some/tail/here") // ["www.techlabs.kz"]
get_host_from_url("www.techlabs.kz") // ["www.techlabs.kz"]
get_host_from_url("www.techlabs.kz/some/tail/here") // ["www.techlabs.kz"]
get_host_from_url("/some/tail/here") // empty (примечание: empty - это атом, а не строка)
get_host_from_url("http://") // empty
get_host_from_url("") // empty
get_host_from_url(undefined) // empty
По-хорошему по данной спецификации нужно написать небольшой тест, но в рамках данной заметки я этого делать не буду. Давайте представим, что при реальной разработке я его написал. А здесь просто опустил :-).
Переходим к самой функции. Начнем обработку с простейшего варианта — когда вместо урла передается значение undefined:
get_host_from_url(undefined) ->
empty;
Теперь добавим обработку урлов, которые начинаются с префикса «http://»:
get_host_from_url(Url) ->
case re:run(Url,"http:\/\/([^/]*)", [{capture,[1], list}]) of
{match, [[]]} ->
empty;
{match, Host} ->
Host;
nomatch ->
empty
end.
Нетрудно заметить, что регулярное выражение, которое используется в функции выше, не охватывает случаи, когда в урле отсутствует префикс «http://». Я не особый любитель сложных регулярных выражений, поэтому не буду даже пытаться искать вариант при котором в рамках одной регулярки можно обрабатывать два варианта строк — с «http://» и без. Вместо этого я хочу сделать дополнительную функцию.
И вот здесь я не знаю как поступить наилучшим образом. Мне в голову пришло такое решение — я создал дополнительную функцию следующего вида:
get_host_from_url(without_http, Url) ->
case re:run(Url, "([^/]*)", [{capture, [1], list}]) of
{match, [[]]} ->
empty;
{match, Host} ->
Host;
nomatch ->
empty
end
Данная функция должна обрабатывать урлы, у которых нет префикса «http://». Нетрудно заметить, что данная функция не обрабатывает «чистые» урлы — у которых имя хоста совпадает с именем урла. Но если урл чистый, то для него никаких проверок не требуется, поэтому я сделал следующую функцию:
get_host_from_url(without_tail, Url) ->
Url.
В итоге у меня получился следующий код:
get_host_from_url(undefined) ->
empty;
get_host_from_url(Url) ->
case re:run(Url,"http:\/\/([^/]*)", [{capture,[1], list}]) of
{match, [[]]} ->
empty;
{match, Host} ->
Host;
nomatch ->
get_host_from_url(without_http, Url);
_ ->
empty
end.
get_host_from_url(without_http, Url) ->
case re:run(Url, "([^/]*)", [{capture, [1], list}]) of
{match, [[]]} ->
empty;
{match, Host} ->
Host;
nomatch ->
get_host_from_url(without_tail, Url)
end;
get_host_from_url(without_tail, Url) ->
Url.
Хочу обратить внимание, что когда я описывал функции по отдельности в секции «nomatch» я указывал значение «empty», а в итоговом варианте я сделал вызов соответствующих функций.
Лично мне полученный код очень нравится. Достаточно прозрачный и чистый. Возможно несколько многословный, но красивый :-). Очень интересно услышать ваше мнение, и, конечно же, любая критика приветствуется.
P.S. я вижу, что в коде есть дублирование и от него, естественно, надо избавиться. Я пока вижу вариант с вынесением обработки регулярного выражения в отдельную функцию, которая выглядит как-то так:
get_host_from_url(by_regexp, RegExp, Url) ->
case re:run(Url, RegExp, [{capture, [1], list}]) of
{match, [[]]} ->
empty;
{match, Host} ->
Host;
nomatch ->
empty
end.