Технологии

Мой вариант функции 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.

RU/KZ