Архив Сентябрь 2009

Тем, кто пользуется перлом регулярно, не надо объяснять, что на этом языке возможно писать программы, которые легко читать, модифицировать и сопровождать. Тем же, кто больше полагается на общественное мнение, но хочет познакомиться с языком, требуется хорошее введение в Perl.

Мне интересно выяснить, что должно быть в таком курсе. За основу я беру главы Camel Book.

Глава 1. Обзор Perl
Первый пример здесь — print "Howdy, world!\n";. Я уже упоминал, насколько противоречивые чувства приходится теперь испытывать с этой фразой, имея 5.10. В Perl 6 это проще (как впрочем и многие другие аспекты).

Подраздел «Стандартный пример»
Здесь приведена программа из 19 строк, содержащая работу с файлами. По своему опыту могу сказать, что знакомство с работой файлов я откладывал как можно дольше (это было в дореволюционные времена на Паскале).

Для начинающих важно быстро запустить программу. Выполнить программу на перле (как и на многих других языках) возможно несколькими способами — однострочником, в интерактивном режиме, либо передав интерпретатору имя файла. Здесь сильное противоречие: с одной стороны, важно не показывать все это разнообразие на первых страницах, с другой — показать как можно раньше, перед тем, как перейти к более серьезным вещам. Равновесие, на мой взгляд, пользоваться интерактивным режимом или однострочными программами до тех пор, пока примеры не станут длиннее :-)

Еще есть вопрос яйца и курицы: с чем знакомить раньше — с оператором присваивания или с переменными. С одной стороны, оператор = стоит в ряду с другими (например, арифметическими, которые работают и с константами), с другой, без переменных он не нужен.

С операторами возникает еще одна неоднозначность: хочется избежать таблиц с приоритетами. Возможно, есть смысл пользоваться подходом, применяемом в грамматиках PGE — вместо явного указания используются относительные приоритеты, например, «умножение сильнее сложения, а деление — такое же».

planetperl.ru

| Нет комментариев

Алексей Капранов сообщил о запуске сайта planetperl.ru.

planetperl.jpg

Сайт представляет собой агрегатор блогов и ЖЖ, в которых авторы пишут о перле на русском языке.

Исходные коды сайта опубликованы на гитхабе.

Хочу поделиться некоторыми мыслями, которые мне хочется воплотить на сайте books.perl.org.

Этот сайт существует много лет, и, прежде всего, пора освежить его внешний вид. Но это одна сторона дела.

Основное же, что меня не устраивает в нынешнем виде, — сайт полностью на английском языке. Мне хочется сделать его многоязычным. Это даст и возможность показывать все переводы книг на другие языки.

Третье — разные издания книг плохо связаны друг с другом или вообще отсутствуют.

Четвертое — для тех книг, которые доступны на Google Books, нужно дать возможность посмотреть их там.

Еще очень хочется найти все обложки в хорошем разрешении.

О том, что собой будет представлять новый сайт, я расскажу через месяц в Пизе.

Организаторы итальянского Perl-воркшопа опубликовали список одобренных заявок на доклады.

Вот некоторые выступления, которые я хочу посмотреть и послушать (в том порядке, в котором они опубликованы на сайте).

  • Joel Bernstein. RESTful HTTP responses with Perl (or, how I learned to stop worrying and love RFC2616)‎
  • Emiliano Bruni. Costruzione di una command line in Perl‎
  • Tim Bunce. State-of-the-art Profiling with Devel::NYTProf‎
  • Tim Bunce. DBI oddmenti
  • Maciej Czekay. ‎How to impress your coworkers, or playing Perl in a team‎
  • Mark Keating. ‎What is Enlightened Perl? What is the Enlightened Perl Organisation?‎
  • Flavio Poletti. I Client Web in Perl‎ 
  • oha. ‎Parse::RecDescent per esempi‎
  • Enrico Sorcinelli. pod2.perl.org: the Perl translation documentation project‎
  • Sue Spence. ‎Scalability 101 for Perl Programmers‎
  • Mike Whitaker. Perl and Unicode‎
  • Jonathan Worthington. ‎The Way To Rakudo *‎
  • Jonathan Worthington. ‎Solved In Perl 6‎

Пятый итальянский Perl-воркшоп пройдет 22 и 23 октября в Пизе.

Появилась новая информация о мероприятиях, которые пройдут в текущем году.

С 30 октября по 1 ноября в Рио-де-Жанейро пройдет YAPC::Brasil. Замечательный повод побывать в Бразилии :-) Правда, сайт мероприятия — только на португальском языке.

5 декабря в Лондоне состоится очередной лондонский Perl-воркшоп. Традиционно лондонский воркшоп не берет плату за вход, но при этом устраивает вечеринку в пабе.

lpw.jpgНапомню также, что 21 ноября в Риге пройдет первый балтийский Perl-воркшоп.

Один из самых неприятных моментов в программировании сайтов, использующих XML, — это обработка кода, пришедшего от визуального редактора.

С одной стороны хочется разрешить коллегам-редакторам не только пользоваться предопределенными действиями, нажимая на кнопки, но и дать возможность ввести нужные HTML-теги, если это необходимо. Но при этом любой некорректный ввод, если его поместить в XML, потенциально способен не только сделать XML невалидным, но в худшем случае сделает неработоспособной и саму страницу с редакторским веб-интерфейсом, лишая возможности исправить ошибку.

Прямолинейное решение — экранировать все угловые скобки, обрамляющие теги, амперсанды и HTML-сущности, а потом обернуть все это в блок CDATA — ненамного лучше полного вырезания тегов, ибо приводит, среди прочего, к тому, что полученный код неудобен для обработки с помощью XSLT. Например, довольно сложно (и даже неоправданно сложно) будет дополнить внешние ссылки классом external:

Читайте об этом на <a href="http://wikipedia.org/">Википедии</a>.

Разумеется, возможно решить задачу, привлекая Perl, но чем сильнее разметка превращается в текст, тем сложнее обрабатывать более сложные случаи.

Все, что нужно сделать, — заставить себя один раз написать валидатор и затем им с удовольствием пользоваться.

Я использую примерно такой вариант. Полученный от пользователя код встраивается в итоговоый XML-документ так, словно он был написан участниками комитета W3C в редакторе Altova XML Spy:

my $xmlParser = new XML::LibXML();
$xmlParser->expand_entities(0);
my $contentFragment =
    $xmlParser->parse_xml_chunk(make_well_formed_xml($content));
$contentNode->appendChild($contentFragment);

Функция make_well_formed_xml() выполняет несколько последовательных преобразований, включая правку с помощью libtidy и замену сущностей:

sub make_well_formed_xml {
    my $content = shift;

    utf8::decode($content);
    my $tidy = HTML::Tidy->new({
        'output-xhtml' => 1,
        'doctype' => 'omit',
        'show-body-only' => 1,
        'fix-uri' => 1,
    });

    $content = $tidy->clean($content);

    $content =~ s{&lt;}{<!ENTITY lt>}g;
    $content = XML::Entities::decode('all', $content);
    $content =~ s{<!ENTITY lt>}{&lt;}g;

    $content =~ s{&}{&amp;}g;

    return $content;
}

Грязные места в коде — декодирование UTF-8 и замену открывающей угловой скобки — конечно нужно заменить более аккуратным кодом, но поскольку все теперь локализовано в единой функции, это можно отложить на потом, и такой код будет работать годами :-)

Отдельно нужно заменить, что правильная настройка libtidy весьма неочевидна, но разобраться в настройках помогут, например, онлайновые интерфейсы.

gabor.jpgГабор Сабо создал на вики сайта Perl Foundation страницу International Perl Resources, где попросил сообщество собрать ссылки на материалы про Perl на разных языках.

В результате за пару дней набралось ссылок на двадцати языках. Русский язык оказался на одном из первых мест по числу ссылок.

Пополнить коллекцию приглашаются все желающие.

Габор Сабо (Gabor Szabo) — активный участник Perl-сообщества, создатель редактора Padre, организатор нескольких мероприятий в Венгрии и Израиле. В 2009 году получил награду White Camel Award.

libapreq2

| Нет комментариев

Как бы ни ругали модуль CGI, я особо никогда не прислушивался к аргументам, потому что пользовался им только тогда, когда учился программированию для веба.

В основном я использовал самописный парсер HTTP-запроса, который проработал почти без изменений больше пяти лет. Парсер умел разбирать все, в том числе и multipart/form-data, хотя и написан он не слишком высокопарно.

Потом, когда пришел UTF-8, потребовался mod_perl, а самописному парсеру стало все труднее справляться с запросами, я заменил его библиотекой libapreq2. Libapreq — это Apache HTTP Server Request Library. Это модуль, который собирается в отдельную библиотеку и подключается к Апачу:

LoadModule apreq_module modules/mod_apreq2.so

Установка весьма простая, однако следует помнить, что для установки сопутствующих Perl-модулей требуется вызывать не ./configure, a perl Makefile.PL:

perl Makefile.PL --with-apache2-apxs=/path/to/apache2/bin/apxs

В каталоге modules появится соответствующие бинарные библиотеки, а Perl получит модули для работы с ней, в частности, Apache2::Request.

Миграция с CGI или любого другого парсера на libapeq2 проходит гладко, поскольку, собственно, и задачи формулируются весьма просто: получить переменную из запроса или прочитать куку.

my $req = new Apache2::Request($this->{'r'});
$p{$_} = $req->param($_) for $req->param();

Так же просто читать куки и еще более просто получать прикрепленные файлы:

my $upload = $req->upload($param);

Итого: если у вас mod_perl, пользуйтесь libapreq.

Тот факт, что Perl был создан лингвистом, не мог не надожить отпечаток и на пользователей языка. Довольно часто разговоры между любителями перла перетекают на обсуждение языков. Не языков программирования, а языков человеческих.

В итальянской монгерской рассылке, например, прошло небольшое обсуждение того, что английской слово free неоднозначно. Неоднозначность вызвана тем, что, как и в русском языке, это слово может переводиться и как свободный, и как бесплатный.

Даккар приводит такие примеры:

VMWare Server è *gratuito*. (VMWare Server бесплатный.)

VirtualBox e qemu sono *liberi*. (VirtualBox и qemu свободные.)

К сожалению, читать итальянскую рассылку mongers@lists.perl.it можно только «вживую», веб-архива у нее нет.

 

Padre

| Комментариев: 2

Вчера вышло очередное обновление написанной на перле интегрированной среды Padre.

padre.jpgСейчас все работает и под Windows, и под Mac OS. Есть и русский перевод (я его обновлю на днях).

padre-code.pngА то, что размер дистрибутива 40 МБ, не должно сильно смущать: в комплекте идут все необходимые компоненты, включая сам Strawberry Perl. Надеюсь, что со временем такого избыточного комплекта не потребуется.

timbunce.jpgTim Bunce — автор модуля DBI — 12 и 13 октября приезжает в Москву, чтобы выступить на конференции HighLoad++.

На вечер 12 октября (понедельник) в «Инфопространстве» запланирована встреча с Moscow.pm. Начало в 19 часов, вход свободный.

Кроме того, Тима в ближайшее время можно увидеть на пятом итальянском Perl-воркшопе в Пизе 22 и 23 октября.

Один из докладов, с которыми он собирается выступить, — Perl Myths. В рассылках про Perl 6 и Parrot сейчас идет обсуждение о том, чем стоит дополнить существующий материал.

На прошлой неделе мне потребовалось автоматически постить RSS в Твиттер.

Первая мысль была — использовать Plagger. Тем более, что именно эту программу ребята использовали на январском хакмите в Москве.

Plagger — это такой механизм, где с помощью конфигурации (в YAML) формируется цепочка, через которую проходят новости, собираемые из разных источников. Всю обработку ведут отдельные плагины: один забирает RSS, другой фильтрует данные, третий размещает их в Твиттере, отправляет по почте или складывает в XML-файлы. Конфигурация может быть самой замысловатой, а отдельных плагинов больше сотни.

Установка оказалась весьма муторной, заняла часа полтора и скачала пол-спана. К тому же, не очень понятно (а документации — кот наплакал), как не пропускать дубликаты записей и как заставить плагины сохранять результаты в базе данных. Необъятное число необходимых модулей заставило задуматься о том, насколько сложно будет переносить скрипты на другой сервер.

В итоге я расчехлил архивы трехлетней давности и все приложение свелось к последовательному вызову нескольких методов:

my $feeds_ref = FeedCrawl::Register::getFeeds();

foreach my $feed (@$feeds_ref) {
    print $feed->id . ' ' . $feed->uri . "\n";
    $feed->fetch();
    $feed->save();
}

Признак старинной архивной копии — вызов print :-)

Ну а пост в Твиттер — тривиальная операция. Все сразу заработало, а через несколько часов окончательно вышел из строя жесткий диск на арендованном у «Мастерхоста» сервере, поэтому скрипт переехал жить и работать во Франкфурт.

Про Дальневосточный Perl-воркшоп, который прошел год назад во Владивостоке, тоже есть что рассказать помимо собственно перла.

Уже то, что мероприятие прошло на другом конце континента, докуда от Москвы лететь восемь часов (а обратно — девять), само по себе волнующе. Это была моя первая вылазка на Дальний Восток.

Зарегистрировалось около 30 человек, в основном, разумеется, из Владивостока. Приехал и Джонатан Вортингтон. К сожалению, никого не было ни из Хабаровска, ни из Японии (хотя, впрочем, из Японии во Владивосток добраться сложнее, чем из Москвы).

Экскурсионная программа (за которую спасибо Vladivostok.pm — Диме, Илье и Роману) для организаторов и гостей включала, не считая прогулок по городу и чаепитий, поездку к Русскому острову и шашлыки на природе с исследованием подземных ходов.

Русский остров — особый пункт для тех, кто любит Гришковца.

 

rus.jpgНе менее интересно было спуститься в заброшенные ходы владивостокских фортов.

fort.jpg

Видеозаписи всех выступлений на Дальневосточном Perl-воркшопе доступны на yapc.tv.

Точка с запятой в перле необязательна, если она приходится на конец блока:

sub doit {
    say 123
}

В отличие от, например, C++, где аналогичная конструкция вызовет ошибку компиляции:

int main() {
   return 0
}

s.cpp: In function 'int main()':
s.cpp:3: error: expected ';' before '}' token

Различие здесь в том, чем точка с запятой является с точки зрения языка. В перле это разделитель инструкций, в C++ — признак окончания выражения.

В JavaScript ситуация еще интереснее: там точки с запятой допустимо опускать не только в конце блока, но и в конце строки:

x += id.value
alert(x)

При этом разрешено продолжать выражение на следующей строке:

x +=
   id.value
alert(x)

Когда компилятор прочитывает очередную строку и не встречает точки с запятой, он делает попытку продолжить инструкцию кодом, записанным на следующей строке. Если при этом обнаружится синтаксическая ошибка, то компилятор возвращается к концу предыдущей строки и считает, что предыдущее выражение закончилось на предущей строке.

Промежуточное резюме: на C++ компилятор строг настолько, что просит явно сообщать, где заканчивается очередная порция кода, а на JavaScript допускаются вольности, но за счет дополнительных действий во время анализа кода.

В перле иногда очень соблазнительно не только опускать точку с запятой в конце блока, но и ключевое слово return в последней инструкции подпрограммы:

sub add {
    my ($a, $b) = @_;
    $a + $b
}

Результат выражения $a + $b — последнее значение, вычисленное в теле подпрограммы — считается результатом, который и нужно передать вызывающему коду.

Выглядит лаконично, не спорю. Но такая экономия на символах обычно оборачивается недоумением при попытках внести в код изменения.

Ситуация очень похожа на рекомендацию ставить запятую после последнего элемента при объявлении хеша:

my %x = (
    alpha => 'a',
    beta => 'b',
);

Перл создает в хеше ровно два элемента, но запятая помогает при добавлении новых элементов (которые часто добавляются копированием и вставкой последней строки). Этот фокус не проходит с яваскриптом: если поставить запятую после последнего элемента массива, то будет создан еще один элемент со значением undefined.

В перле отсутствие точки с запятой в конце строки может привести к тому, что ее отсутствие не будет замечено, когда после этой строки потребуется дописать новые инструкции:

sub add {
    my ($a, $b) = @_;
    $a + $b
    warn $a;
}

Даже на время отладки не хочется искажать написанный ранее код, поэтому пропущенная точка с запятой просится в начало строки:

sub add {
    my ($a, $b) = @_;
    $a + $b
;    warn $a;
}

Такие трюки с пропущенной запятой — один из приемов, которые малыми шажками ведут код к read-only-модели.

Например, продолжая такую идеологию возможно было бы записать функцию так:

sub add {
    shift() + shift
}

Но лучше так не делать. Или только ради развлечения и изучения возможностей языка.

Организаторы прошлогоднего хорватского Perl-воркшопа устроили для докладчиков замечательную экскурсию.

Точнее, докладчики были и с мероприятия DORC/CLUC, к которому был пристыкован воркшоп. Мы поехали смотреть на Плитвицкие озера в полутора-двух часах езды от Загреба.

Этот комплекс — каскад из нескольких озер, которые расположены на разной высоте и перетекают друг в друга. Вдоль озер ведут деревянные мостки.

plitvicka-jezera.jpg

Внизу находится очаровательный семидесятидвухметровый водопад.

plitvicka-jezera-waterfall.jpg

На обратном пути мы заехали еще в одно живописное место «Под Растоцким кровом», расположенное на месте слияния двух рек. 

_MG_4358.jpgПравда, на обратном пути вместо поворота на Загреб мы повернули в другую сторону и через час опять оказались у озер :-) Впрочем, в Хорватии на озера можно смотреть бесконечно.

Какой вывод должен сделать читатель? А такой: участвуйте в Perl-мероприятиях, в том числе и в качестве докладчиков.

В мае мы провели первый Perl-воркшоп в Узбекистане «Перл Ташкент». Я неоднократно рассказывал о нем на мероприятиях, но не здесь :-)

Воркшоп состоялся в рамках мероприятия Best Soft Uzbekistan — 2009. На подходе к помещению висел огромный плакат с расписанием, а снизу на нем — упоминание о воркшопе.

perluz.jpg

Интересно, что организаторы, при всей серьезности, не постеснялись написать слово «хаки» в названии одного из докладов.

«Бестсофт» проводился на территории городка Ташкентского университета информационных технологий. Наше помещение было в отдельностоящем корпусе. Вечером мы разглядывали это здание с верхней площадки телебашни. Вон оно, с красной крышей:

perluz-venue.jpg

Несмотря на то, что докладчиков было только двое (они же — организаторы), слушателей набралось около тридцати.

perluz-people.jpg

Фото Алексея Капранова

Интересно, что на Perl-воркшоп пришли и люди, для которых Perl не является основным языком (обратите внимание на то, что написано на футболке):

perluz-alisher.jpg

Лично мне все понравилось. А видео будет немного позже.

Попробуем написать программу для того, чтобы из командной строки получать взаимный курс любой пары валют.

Исходные данные: имеем API, возвращающее взаимный курс валют на любую запрошенную пару, например, USD/EUR. На выходе должны получить одно-единственное число — сегодняшний курс.

Пару валют предполагается вводить в командной строке как аргументы при запуске скрипта:

$ rate USD EUR

По умолчанию предполагается, что курс относится к рублю:

$ rate USD

Поскольку скрипт должен работать с любой современной версией языка, use 5.10 и say использовать не получится (а жаль).

#!/usr/bin/perl

Потребуется модуль для загрузки страницы с сервера:

use LWP::Simple;

Проверяем число аргументов в командной строке или печатаем пример использования:

if (length @ARGV < 1) {
    print "Usage: rate FROM [TO]\n";
    exit;
}

Если нашлолсь два аргумента, то считаем их запрашиваемой парой валют:

my ($from, $to) = @ARGV;

Отсутствующий второй аргумент заменяем валютой по умолчанию:

$to = 'RUB' unless $to;

API ожидает получить названия валют в верхнем регистре:

$from = uc $from;
$to = uc $to;

Делаем простую проверку на то, что коды валют более или менее правильные и содежат ровно три буквы (хотя это можно было сделать и до преобразования в верхний регистр):

unless ($from =~ /^[A-Z]{3}/ && $to =~ /^[A-Z]{3}/) {
    print "Wrong currency code requested\n";
    exit;
}

И, наконец, происходит запрос к API с нужными параметрами:

my $data = get("http://whoyougle.com/money/api/$from/$to");

Ответ приходит в виде XML, из которого регулярными выражениями (а зачем в таком простом примере делать сложнее?) отыскивается и выбирается число с курсом.

my ($result) = $data =~ /<value>(.*?)<\/value>/;

print "$result\n";

Просто? Да. Переносимо? Более или менее. Поймет ли начинающий? Однозначно.

P. S. Идею такого скрипта предложил Дима Карасик.

P. S. II. В этой простой программе мне интересно было прежде всего то, как подойти к обучению перлу на реальных задачах, но при этом использовать минимум дополнительных компонентов, которые необходимо либо изучить, либо установить на компьютер до того, как удастся решить простейшие задачи. Об этом я как-нибудь напишу отдельно. Я днем и ночью думаю о том, как сделать перл доступным :-)

Поломка жесткого диска на сервере infolavka.ru подтолкнула заодно перейти на новую версию перла.

Сам Perl 5.10.1 поставился без каких-либо сложностей за один присест.

# perl -v

This is perl, v5.10.1 (*) built for i686-linux

Copyright 1987-2009, Larry Wall

Разумеется, начали вылезать несовместимости с модулями, поставленными еще для 5.10 (а кое-где даже для 5.8.8, доставшегося от комплекта операционной системы). Но в итоге все заработало и в серверных логах появляются записи только об ошибках 404 из-за неверных запросов.

На сайте Карла Мэсака есть интересное руководство про то, как создавать программы для Perl Golf.

В качестве основы взята задача о расстановке восьми ферзей на шахматной доске.

На этом примере показано, как, написав вначале «многословный» код, методично сократить его. На каждом шаге показаны версии до и после преобразований.

На первом шаге убираются все комментарии. На втором — имена переменных и названия подпрограмм сокращаются до одной буквы. На третьем — циклы while заменяются вызовами функции grep. На четвертом код подпрограмм переносится в основную программу, что дает экономию на вызовах и определении фукнций. На пятом объединяются в цепочки условия в вызовах grep и вывод в аргументах print. На шестом шаге части кода, в которых использовался условный оператор if, объединяются в одну цепочку с помощью or.

Седьмой шаг, довольно хитрый, меняет направление счетчика в рекурсивном вывове функций. Вместо последовательного декремента счетчика он наоборот инкрементируется. Это позволяет не передавать начальное значение при первом вызове. То есть вместо

sub g {
   my $l = shift;
   . . .
   ($l > 1) and !g($l - 1)
   . . .
}
g(8);

получается следующее:

sub g {
   my $l = shift;
   . . .
   ($l < 7) and !g($l + 1)
   . . .
}
g;

На восьмом шаге происходят тонкие замены логических операторов. На девятом находится применение «секретному» оператору «детской коляски» (babycart) @{[]}. На десятом происходит еще одна смена направления — однако, на этот раз в списке из одного элемента: операция shift заменена вызовом pop. На одиннадцатом исчезает инструкция use strict. И, наконец, после двенадцатого шага пропадают необязательные пробелы:

sub g{my$l=pop;for$c(1..8){my$d;grep++$d==abs$c-$_|$c==$_,@_
or$l<7&!g($c,@_,$l+1)||print "@{[$c,@_]}\\n"}}g

P. S. О том, как решалась задача на YAPC::Russia::Golf 2009, рассказано в ЖЖ Андрея Завьялова.

В «Московском доме книги» на Новом Арбате сейчас продаются аж десять (!) книг про Perl.

perlbooksoffline.jpg

Э. Рандал, Д. Сугальск, Л. Тёч. Perl 6 and Parrot Essentials (2-е издание) (устарела)

Л. Штайн. Разработка сетевых программ на Perl

Л. Уолл, Т. Кристиансен, Д. Орвант. Программирование на Perl (3-е издание)

Р. Шварц, б. фой, Т. Феникс. Perl: изучаем глубже

Р. Фоули. Perl отладчик

К. Мельтцер, Б. Михальски. Разработка CGI-приложений на Perl (устарела)

М. Шохирев. Язык программирования Perl 5

А. Ломов. Apache, Perl, MySQL: практика создания динамических сайтов. Самоучитель

Д. Фридл. Регулярные выражения (3-е издание)

Н. Прохоренок. Разработка Web-сайтов с помощью Perl и MySQL (крайне не рекомендую)

Несколько слов о том, как работает импорт валют на сайте infolavka.ru.

Раздел с информацией о курсах валют — один из моих любимых (с технической точки зрения). Мы отображаем актуальные курсы 130 валют и ведем историю 100 валют, начиная с 1998 года (когда-нибудь откроем и более раннюю).

Самое интересное — импорт курсов. Ситуация простая: есть две с половиной сотни стран, у которых полторы сотни разных валют, и соответственно, много банков-эмитентов. Банки самые разные, и далеко не у всех имеются веб-сервисы для того, чтобы автоматически получать обновления курсов. Из сотни валют мы нашли только 14 банков, которые удобны для скриптов.

Разумеется, даже в этих 14 случаях формат данных ни разу не повторяется. Одни банки предлагают XML (с уникальной структурой), другие — RSS-потоки, третьи публикуют данные в CSV, четвертые — в текстовых таблицах. Некоторые банки размещают данные в PDF или просто HTML, но мы их сейчас не читаем. Самый необычный формат доставки, которым мы пользуемся, — у банка Казахстана: он рассылает данные по электронной почте.

Моя первая версия скрипта-фетчера, собирающего информацию из двух или трех банков, пользовалась только XML-экспортом. Несколько XSLT-файлов преобразовывали XML из разных форматов в единый, который затем парсился перлом.

На втором подходе к задаче появились отдельные модули, импортирующие конкретный банк:

use Infolavka::Tools::Money::Fetch::BYR;
use Infolavka::Tools::Money::Fetch::CAD;
use Infolavka::Tools::Money::Fetch::CHF;
use Infolavka::Tools::Money::Fetch::CZK;
use Infolavka::Tools::Money::Fetch::EEK;
use Infolavka::Tools::Money::Fetch::EUR;
use Infolavka::Tools::Money::Fetch::ILS;
use Infolavka::Tools::Money::Fetch::LTL;
use Infolavka::Tools::Money::Fetch::LVL;
use Infolavka::Tools::Money::Fetch::RUB;
use Infolavka::Tools::Money::Fetch::RON;
use Infolavka::Tools::Money::Fetch::GEL;
use Infolavka::Tools::Money::Fetch::IRR;
use Infolavka::Tools::Money::Fetch::KZT;

Скрипт импорта обходит банки и обновляет соответствующие таблицы в базе данных. Если запрос на какой-то из банков сбоит, используются данные, полученные в последний удачный раз:

for my $code (@codes) {
    warn "Fetching exchange rates for $code...\n";

    my $fetcher =
       eval "new Infolavka::Tools::Money::Fetch::$code";
    my $data = 
       $fetcher->fetch_current // get_last_available($code);

    warn "ok\n" if defined $data;

    push @{$items{$code}}, @$data if defined $data
}

(Обратите внимание, как Леша применил оператор defined-or.)

Итог работы можно видеть на страницах с курсами за текущий день в виде чисел или на отдельных страницах в виде графика с историей курса.

Отдельное удовольствие доставляет возможность играться с имеющимися данными и отображать их в разных видах, например, в виде облака валют (есть еще куча задумок).

%+ и %-

| Нет комментариев

В Perl 5.10 появилась возможность давать имена сохраняемым совпадениям в регулярных выражениях. Чтобы эффективно пользоваться этим нововведением, необходимо разобраться в том, как работают хеши %+ и %-, куда попадают совпавшие фрагменты.

Именованные скобки в регулярных выражениях — это конструкция вида (?<name>...). Если сопоставление оказалось успешным, то к искомому фрагменту возможно обратиться через элементы хешей %+ и %-. Например, в этом примере совпадение будет доступно в переменной %+{name}.

Сложности начинаются, если регулярное выражение составлено таким образом, что в нем оказывается несколько скобок с одинаковым именем. Такое (если это сделано намерено) может произойти в том случае, когда автор выражения, например, предполагает, что на реальных данных должен совпадать только один именованный фрагмент. В другом случае регулярное выражение может быть составлено из отдельных подвыражений: здесь возможны как многократные вхождения, так и многократное сопоставление в цикле.

Вот пример, где из строки 120 EUR in USD, please извлекают названия валют.

use v5.10;
use strict;
use Data::Dumper;

my $string = '120 EUR in USD, please';
my @currencies = $string =~ /(?<currencyName>[A-Z]{3})/g;
say Dumper(\@currencies);
say Dumper(\%+);
say Dumper(\%-);

Именованное выражение ?<currencyName> встречается один раз, но обрабатывается в цикле. В массиве @currencies окажется два элемента:

$VAR1 = [
          'EUR',
          'USD'
        ];

А в хешах %+ и %- будет лишь по одному:

$VAR1 = {
          'currencyName' => 'USD'
        };

$VAR1 = {
          'currencyName' => [
                              'USD'
                            ]
        };

Важно обратить внимание на то, что в эти хеши попала последняя совпавшая подстрока.

Теперь вынесем часть регулярного выражения в отдельную переменную и дважды применим ее в сохраняющих скобках с разными именами:

my $re = '[A-Z]{3}';
$string =~ /(?<from>$re).*(?<to>$re)/;
say Dumper(\%+);
say Dumper(\%-);

Поскольку сейчас имена не пересекаются, все совпадения окажутся доступными:

$VAR1 = {
          'to' => 'USD',
          'from' => 'EUR'
        };

$VAR1 = {
          'to' => [
                    'USD'
                  ],
          'from' => [
                      'EUR'
                    ]
        };

Обратить внимание, кстати, стоит на то, что в хеше %- поименованные элементы всегда содержат списки, даже если они состоят из одного элемента.

Наличие списков, однако, помогает, когда одноименных элементов становится больше:

$string =~ /(?<currencyName>$re).*(?<currencyName>$re)/;
say Dumper(\%+);
say Dumper(\%-);

В этом примере хеш %+ вновь содержит один элемент — последнее совпадение, но в ключе $-{currencyName} теперь список элементов из двух строк:

$VAR1 = {
          'currencyName' => 'EUR'
        };

$VAR1 = {
          'currencyName' => [
                              'EUR',
                              'USD'
                            ]
        };

Ничто не мешает вынести и само имя фрагмента в отдельную переменную:

my $named = '?<currencyName>[A-Z]{3}';
$string =~ /($named).*($named)/;
say Dumper(\%+);
say Dumper(\%-);

Результат будет таким же, как и в предыдущем случае:

$VAR1 = {
          'currencyName' => 'EUR'
        };

$VAR1 = {
          'currencyName' => [
                              'EUR',
                              'USD'
                            ]
        };
Важно помнить, что если именованное выражение было необязательным и не совпало, его следы все равно будут присутствовать в хеше %-:

$string = "120 EUR, please";
$string =~ /($named).*($named)?/;
say Dumper(\%-);

Этот пример вернет хеш со списком из строки и значения undef:

$VAR1 = {
          'currencyName' => [
                              'EUR',
                              undef
                            ]
        };

Порядок элементов списка совпадает с порядком именованных скобок в регулярном выражении.

Страницы

  • img

Об архиве

Страница содержит архив записей за Сентябрь 2009, расположенных по убыванию.

Август 2009 — предыдущий архив.

Октябрь 2009 — следующий архив.

Смотрите новые записи на главной странице или загляните в архив, где есть ссылки на все сообщения.