Вчера двухсполовинойлетний Даниил Андреевич нарисовал вот такую штуку.

Чтобы снять неясность о том, что родитель увидел в рисунке своего ребенка, привожу рядом другую картинку — логотип с pm.org.

Вчера двухсполовинойлетний Даниил Андреевич нарисовал вот такую штуку.

Чтобы снять неясность о том, что родитель увидел в рисунке своего ребенка, привожу рядом другую картинку — логотип с pm.org.

Теперь настало время передвать более сложные аргументы, чем простые строки. Нет ничего проще — use Storable.
Перед тем, как передать задачу воркерам, упаковываем аргументы:
my $c = 0;
for (qw (red orange yellow green blue violet)) {
$tasks->add_task(
'search' => freeze([$c++, "$_ $text"]),
{
on_complete => \&search_completed
}
);
}
А внутри функции воркера — распаковываем:
my $request = thaw($job->arg);
my $result = MySearch::search($request->[1]);
После того, как получен результат, с ним потребуется произвести те же манипуляции, но в обратную сторону: заморозить (freeze) результат, вернуть серверу gearmand, а на клиентре — растопить (thaw).
Правда или нет, но передача данных от воркера в вызывающую задачу — глючит (особенно в Gearman::XS::Worker), да и бесконечные freeze/thaw только уродуют код. Намного удобнее, если воркер не будет ничего возвращать, а вместо этого складировать свои результаты в общее хранилище (например, в базу данных), доступное вызывающему приложению.
Переделка скрипта для работы с Gearman весьма проста, если решение задачи запрограммировано таким образом, чтобы у нее были только вход и выход :-) В случае со вчерашним примером функция поиска, помещенная в воркер, состоит из двух строк:
sub search {
my $job = shift;
return MySearch::search($job->arg);
}
Клиентский код несколько усложняется: сначала необходимо создать пул задач, а затем дождаться их выполнения:
for (qw (red orange yellow green blue violet)) {
$tasks->add_task(
'search' => "$_ $text",
{
on_complete => \&search_completed
}
);
}
$tasks->wait;
Сбор данных по окончании каждой задачи тоже крайне прост:
sub search_completed {
my $result = shift;
$html_result .= $$result;
}
Теперь осталось запустить с десяток отдельных воркеров, и скорость работы тестового скрипта возрастает на порядок: последовательные запросы на поиск картинок шести цветов зарубежной радуги завершались за 1-3 секунды, после модернизации — за 0,1-0,6. Задачи параллелятся на ура, но важно понимать, что это имеет смысл только для тех задач, которые по своей природе проводят много времени в ожидании ответа, не тратя процессорное время локальной машины.
На болгарском Perl-воркшопе я хочу немного рассказать о том, как на практике обрабатывать задачи, используя Gearman.
Один из примеров — последовательная трансформация кода: сначала цикл, потом параллельный вызов нескольких воркеров, затем попытка использовать внутри воркеров общие данные.
Отдельная задача минимальна: для заданного запроса $text найти на гугле несколько картинок разного цвета. В первом случае, когда используется цикл, работает такой код:
for (qw (red orange yellow green blue violet)) {
$html_result .= MySearch::search("$_ $text");
}
Я хочу показать, как эти запросы распараллелить, передав их разным воркерам, работающим на разных машинах. А следующий шаг — научить независимые воркеры использовать общие данные, используя либо memcached, либо дополнительный неблокирующий воркер.
Объявлены даты проведения конференции YAPC::Europe 2010. Конференция пройдет в Пизе с 4 по 6 августа. Место проведения — конференц-центр гостиницы Moh Galilei. Тема — The Renaissance of Perl.
Еще в прошлом веке университетский товарищ посоветовал мне почитать книгу о том, как программировать игры. Ни автора, ни названия я не помню, но очень хорошо запомнил, что простыми вычислениями можно запрограммировать полет мухи вокруг лампочки, а еще несколькими строками сделать траекторию ее полета такой, что возникнет впечатление, что муха летает осмысленно.
На прошлой неделе я купил книгу создателя Wolfram|Alpha Стивена Вольфрама A New Kind of Science. Книга, конечно, объемная (1200 страниц), и всю ее я еще не прочитал, но основная мысль заключается в том, что большинство, если не все, явлений в природе возможно запрограммировать последовательностью простых вычислений.
Тот рассказ о программировании полета мухи — а я его тогда проверил на C++ — я всегда помню и стараюсь пользоваться простыми расчетами, если это возможно.
Лингвистические задачи, для которых Perl обычно очень удобен, часто можно свести к набору простейших подзадач. Вот пример того, как я генерирую фразы-подсказки поисковых запросов к конвертеру валют.
Первым делом нужно задать набор грамматик, по которым следует формировать случайные фразы:
my @grammars = (
'RateWordNoun CurrencyNameGenitiveA',
'RateWordAdjective CurrencyNameNominativeA',
'HowMuchCurrencyNameWithValueA',
'Value CurrencyCodeA ConvertWord CurrencyCodeB',
'CurrencyNameWithValueA ConvertWord
CurrencyNamePluralLocativeB'
);
Каждое слово в строке отдельной грамматики — вызов одноименной функции; каждая из них тоже простейшая:
sub RateWordNoun {
return $rateWordNoun[int rand scalar @rateWordNoun];
}
sub RateWordAdjective {
return $rateWordAdjective[int rand scalar @rateWordAdjective];
}
Эти функции используют небольшой набор слов:
my @rateWordNoun = ('курс');
my @rateWordAdjective = ('сколько стоит', 'почем');
Однако это не мешает делать и сколь угодно длинные списки:
my @currencyCode = qw(
RUR EUR USD JPY CHF GBP UAH KZT CAD
AUD BYR DKK ISK NOK SEK CNY TRY SGD ILS
);
my @currencyNameNominative =
('рубль', 'евро', 'доллар США', 'японская иена',
'швейцарский франк', 'фунт стерлингов',
'украинская гривна', 'казахстанский тенге',
'канадский доллар', 'австралийский доллар',
'белорусский рубль', 'датская крона',
'исландская крона', 'норвежская крона',
'шведская крона', 'китайский юань',
'турецкая лира', 'сингапурский доллар', 'шекель');
Собственно, остается написать функцию, которая выбирает случайную грамматику и строит случайную фразу по заданным правилам.
sub random {
my $template = $grammars[int rand scalar @grammars];
$template =~ s{(\w)\b}{$1()}g;
$template =~ s{([AB]\b)\(\)}{(\$$1)}g;
$template =~ s{\s+}{ . ' ' . }g;
return eval $template;
}
Блюдо готово.
курс швейцарского франка
сколько стоят 2 шведские кроны
курс японской иены
29 UAH в TRY
15 сингапурских долларов в норвежских кронах
почем турецкая лира
сколько стоят 36 евро
почем шекель
8 CHF в SEK
51 китайский юань в белорусских рублях
360 EUR в AUD
82 шекеля в рублях
600 долларов США в австралийских долларах
Cравнить два списка — задача, которую без привлечения модулей (например, List::Compare) в перле решать не очень удобно.
Начинающих может удивить, что выражение @a == @b сравнивает не сами списки, а их размер.
С приходом Perl 5.10 возникает соблазн использовать для сравнения двух списков оператор смарт-матчинга (~~):
say @a ~~ @b;
Это хорошо работает до тех пор, пока в списках содержатся скаляры, например:
my @a = (1, 3, 5);
my @b = (1, 3, 5);
say @a ~~ @b; # 1
Однако следует помнить, что (по крайней мере, начиная с версии 5.10.1), операция сопоставления списков последовательно сопоставляет соответствующие элементы списков, используя тот же оператор ~~.
Поэтому следующий пример тоже напечатает единицу, хотя исходные списки — разные:
my @a = (1, 3, 5);
my @b = (1, qr/\d/, 5);
say @a ~~ @b; # 1
Сегодня Билл Гейтс и OnPerl.ru завели себе по аккаунту в Твиттере.
До сегодняшнего дня я не задумывался о том, что действия с оператором defined-or (//) возможно объединять в цепочки, и это весьма удобно.
Например:
my $ip =
$ENV{X_HTTP_FORWARDED_FOR} //
$ENV{HTTP_X_REAL_IP} //
$ENV{REMOTE_ADDR};
Соответственно, добавить новый вариант и одновременно установить нужный приоритет значению, очень просто.
my $ip =
$page->param('request_ip') //
$ENV{X_HTTP_FORWARDED_FOR} //
$ENV{HTTP_X_REAL_IP} //
$ENV{REMOTE_ADDR};
Тут на днях бот MSN походил по сайту cpantesters.org, да так активно, что администраторы его забанили, о чем сообщили в посте под заголовком «MSNBOT must die!».
(Про это написали и на linux.org.ru, причем аж дважды, хотя в комментариях никакого интересного срача.)
В сообщении на blogs.perl.org говорится, что каждые несколько секунд поступали запросы с 20-30 IP-адресов MSN (Microsoft in their incompetent wisdom decided to unleash 20-30 bots every few seconds). Собственно, эта фраза ничего не разъясняет о том, как же часто на самом деле были запросы. 30 пользователй за несколько секунд — это же нормально. Я погрепал логи своих сайтов, и увидел, что клиент со строкой USER_AGENT msnbot-media/1.1 (+http://search.msn.com/msnbot.htm) действительно ходит часто и со многих IP, но не чаще чем раз в секунду (обычно еще реже). Максимум, что удалось увидеть — два запроса за секунду, причем один из них — к файлу robots.txt.
Cpantesters приняли совершенно неразумное решение и заблокировали диапазон IP-адресов /^65\.55\.(106|107|207)/, записанный за Microsoft Corp, и, соответственно, бот MSN (читай: Bing) не сможет индексировать cpantesters.org. Впрочем, bing.com на запрос cpan testers пока еще дает релевантный ответ.
С одной стороны, Perl-сообщество открывает конкурс Iron Man, чтобы популяризировать Perl именно через индексирование записей в поисковых системах, а с другой, — закрывает один из них.
Предлагаю внести во все конфигурационные файлы еще и такие строки:
RewriteCond %{HTTP_REFERER} php [NC]
RewriteRule .* /404 [R,L]
Геометрические построения на поверхности Земли — хитрая штука, которая выглядит необычно, когда ее проецируют на плоскость. Кратчайшее расстояние между двумя городами в большинстве случаев превратится в дугу, по разному изогнутую в северном и южном полушариях. Прямыми останутся только линии, прочерченные с севера на юг.
На спане есть модуль с длинным названием Geo::GoogleEarth::Pluggable::Plugin::GreatCircle, который помогает рассчитать траекторию: по двум заданным точкам он формирует список точек, которые отрезками прямых с той или иной точностью рисуют кратчайший путь по поверхности планеты.
Эта программа печатает координаты 93-х точек между Москвой и Киевом.
use v5.10;
use strict;
use Geo::GoogleEarth::Pluggable;
my @moscow = (55.7522, 37.6156);
my @kiev = (50.4333, 30.5167);
my $arc =
(new Geo::GoogleEarth::Pluggable)->GreatCircleArcSegment(
startPoint => {lat => $moscow[1], lon => $moscow[0]},
endPoint => {lat => $kiev[1], lon => $kiev[0]}
);
for (@{$arc->coordinates}) {
say $_->lat, ', ', $_->lon;
}
Точность построения (иными словами, число точек и длина отрезков) возможно регулировать параметром span, переданным при вызове метода GreatCircleArcSegment. По умолчанию путь разбивается на участки длиной по 10 километров.
В ходе расчета пути выполняются другие интересные геометрические задачи, например, поиск расстояния между двумя точками, или поиск точки, которая удалена от данной на заданное расстояние в нужном направлении.
Модуль в действии дает результаты, которые иной раз кажутся невозможными (как то лететь из Казахстана в Америку через Северный полюс), но если проложить кратчайший путь на реальном глобусе, то все сходится.
А вот как выглядит путь Харьков — Хабаровск.

20 февраля в Екатеринбурге состоится пятый российский Perl-воркшоп «Perlburg».
Мероприятия организовано математико-механическим факультетом УрГУ. Участие бесплатное.
Perlburg — 16-е мероприятие в серии YAPC::Russia, проводимое совместно с Moscow.pm. С 2007 года мы провели воркшопы и конференции в семи странах: России, Украине, Белоруссии, Узбекистане, Казахстане, Болгарии и Латвии.
Разбор строки поискового запроса — задача, которая быстро превращается в головную боль, как только захочется ввести синтаксис запросов. Если фразы типа +театр -большой разобрать еще просто, то при обработке запроса театр & ((малый & москва) | (большой & урюпинск)) без грамматики не обойтись.
В январе 2010 на спане появился модуль Search::Query, который на первый взгляд поможет разрулить некоторые проблемы разбора поисковых запросов и даже подготовить подзапросы для последующих обращений к собственно поиску и склеиванию методами MapReduce.
Модуль заменяет простую грамматику поискового запроса, предоствляя в замен несколько методов, которые позволяют быстро представить запрос в виде древовидной структуры.
Разбор строки выполняет инстанс $parser типа Search::Query::Parser. (Сам класс Search::Query не содержит рабочую логику.)
use Search::Query;
my $parser = Search::Query->parser();
Затем вызывают метод parse, передав ему поисковый запрос.
my $query = $parser->parse('большой театр');
Возвращаемое значение — значение типа Search::Query::Dialect::Native. Оно может быть преобразовано в строку (это поможет, например, визуально проконтролировать правильность разбора), хотя в простейшем случае она не сильно отличается от запроса:
+:большой +:театр
Кроме того, возможно пройтись по построенному дереву запроса, вызвав метод walk():
say $query->walk(\&my_walk);
Функция, ссылку на которую получает метод walk(), получает четыре параметра, содержащие объяснение частей запроса.
sub my_walk{
my ($clause, $dialect, $code, $prefix) = @_;
. . .
}
Модуль Search::Query позволяет создавать и собственные диалекты поисковых языков, наследуя их от Search::Query::Dialect, либо определяя типовые конструкции при создании парсера:
my $parser = Search::Query->parser({
and_regex => 'И',
or_regex => 'ИЛИ',
not_regex => 'НЕ',
});
Разумеется, ключевые слова возможно описывать и регулярными выражениями. Дополнительные параметры конструктора парсеров задают правила для слов пользовательского запроса, разделителей и т. д.
Знаете ли вы, что у слова hash есть и другое значение? :-)
В Амстердаме, например, в сувенирных магазинах можно купить чупа-хеш.

Сегодня в рассылке итальянских Perl-монгеров появилось вот такое письмо (оригинал требует регистрации):
Scusate la domanda stupida che mi permetto di fare a voi esperti. Sto studiando
il PERL per l'applicazione OTRS. Ho notato che molti moduli finiscono con un
'1;' Ha un significato ben preciso? Una label? Un return-code? Ho provato a cercare, ma purtroppo con scarso successo! Grazie dell'aiuto.Простите за глупый вопрос, который я хотел бы задать вам экспертам. Я изучаю Perl по приложению OTRS. Заметил, что многие модули заканчиваются на '1;'. Это имеет какой-то смысл? Метка? Код возврата? Я попробовал поискать, но, к сожалению, неудачно! Спасибо за помощь.
Несколько человек сразу же с радостью ответили на вопрос. Меня же этот пост заинтересовал в первую очередь тем, что в нем затронут хитрый момент, который важно объяснить начинающим.
Про эту единицу я спрашивал старшего коллегу десять лет назад — когда впервые ее увидел, было совершенно непонятно, зачем она нужна. (Впрочем, сейчас, когда понятны требования языка, становится понятно, что true после загрузки модуля в общем-то не нужен.)
1; — то, про что обязательно нужно писать в книгах для начинающих отдельным абзацем.
Разумеется, допустимо возвращать не только единицу, а любое число, преобразующееся в true. Необычные варианты собраны на странице returnvalues.useperl.at/values.html. Самое частое значение — 42 :-)