четверг, 1 октября 2009 г.

Reduced map

Как известно в Perl 6 имеются гипер- и редакшноператоры
(заметка о них http://laziness-impatience-hubris.blogspot.com/2009/01/perl6.html).

Но что же делать в Perl 5?

Для замены редакшноператоров можно воспользоваться подпрограммой reduce из модуля List::Util или
воспользоваться "reduced map" (rmap) из нижеприведенного модуля (List::Rmap).
Замена же гипероператоров осуществляется при помощи "hyper map" (hmap) того же модуля.

Рассмотрим примеры.

use strict;
use warnings;

use List::Rmap qw(reduce rmap hmap);

my @a = (1, 2, 3);
my @b = (4, 5, 6);

print reduce { $a + $b } @a;
# Бyдет напечатано 6
# Это в Perl6 эквивалентно [+] @x
print "\n";

# reduced map
print join " ", rmap { $a + $b } @a;
# Бyдет напечатано 1 3 6
# Это в Perl6 эквивалентно [\+] @x
print "\n";

# hyper map
print join " ", hmap { $a + $b } @a, @b;
# Бyдет напечатано 5 7 9
# Это в Perl6 эквивалентно @x >>+<< @y


А вот содержимое вышеупомянутого модуля List::Rmap:

# Reduced map

package List::Rmap;

use strict;
use warnings;

no strict qw(refs);

use Exporter;
our @EXPORT_OK = qw(reduce rmap hmap);


sub import {

# Avoid "Name "..." used only once" warnings for $a and $b.
my $caller = caller;
local *{"${caller}::a"};
local *{"${caller}::b"};

goto &Exporter::import
}



sub reduce (&@) {
my $code = shift;

my $caller = caller;
local *{"${caller}::a"} = \ my $a;
local *{"${caller}::b"} = \ my $b;

$a = shift;
foreach (@_) {
$b = $_;
$a = &{$code}();
}

return $a;
}



sub rmap (&@) {
my $code = shift;

my $caller = caller;
local *{"${caller}::a"} = \ my $a;
local *{"${caller}::b"} = \ my $b;

my @r = ($a = shift);
foreach (@_) {
$b = $_;
push @r, $a = &{$code}();
}

return @r;
}



sub hmap (&\@\@) {
my $code = shift;
my ($ra, $rb) = @_;

@$ra == @$rb or die "Different length of lists.";

my $caller = caller;
local *{"${caller}::a"} = \ my $a;
local *{"${caller}::b"} = \ my $b;

my @r = ();
foreach ($[ .. $#$ra) {
$a = $$ra[$_];
$b = $$rb[$_];
push @r, &{$code}();
}

return @r;
}


1;

пятница, 25 сентября 2009 г.

Parrot: Request Tracker и Trac Tracker

Разработчики Parrot отказались от использования Request Tracker в пользу Trac Tracker. А на душе остался осадок: ведь первый написан на Perl, а второй на Python.

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

Но может в Trac есть нечто, о чем я не знаю?

пятница, 18 сентября 2009 г.

Списки

Иногда забываю о таких замечательных модулях как List::Util и List::MoreUtils.
Почему забываю? Потому, что маленькие полезные подпрограммы по работе со списками из этих модулей легко и быстро реализовать самому.

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


use strict;
use warnings;

use List::Util;

use Benchmark qw(cmpthese);

my $foo = [1 .. 10000];

use vars qw($a $b);

cmpthese(1000, {
'manual' => sub {
my $s = 0;
$s += $_ foreach @$foo;
},
'List::Util::sum' => sub {
List::Util::sum(@$foo);
},
'List::Util::reduce' => sub {
List::Util::reduce { $a + $b } @$foo;
},
});

__END__

Rate manual List::Util::reduce List::Util::sum
manual 153/s -- -11% -91%
List::Util::reduce 171/s 12% -- -90%
List::Util::sum 1662/s 987% 871% --

четверг, 10 сентября 2009 г.

Url, обработчики и диспетчера

Url и обработчики

Первоначально web сайты целиком состояли из статических HTML страниц. Затем появился CGI, и сайты стали обзаводиться формами отправки данных и прочими динамическими станицами. Постепенно процент динамических страниц все увеличивался и увеличивался. Но url вида http://www.server.com/cgi-bin/foo.cgi не индексировались поисковиками, а также страдала эстетика, поэтому динамические страницы стали маскировать под обычные html: http://www.server.com/foo.html.

В то же время шло усложнение CGI скриптов, и повторяющийся код стали выносить в общие модули. Но не весь такой код можно было вынести в модули, в скриптах оставался код простой инициализации. Проще иметь одну точку входа, в которой посредством диспетчера вызывались обработчики конкретных команд, url.

Кстати, одну точку входа имели изначально те, что прятал статику за динамикой при помощи назначения обработчиков в Apache, а не при помощи mod_rewrite. Для тех, кто не прятал динамику за статикой, единая точка входа приводила к не слишком красивым url: http://www.server.com/index.php?action=foo, хотя при помощи mod_rewrite этого можно избежать.

Далее в статье web сайты являются лишь частным случаем.

Обработчики и диспетчера

В perl есть несколько возможностей сообщить диспетчеру какой код необходимо выполнять для каждой конкретной команды (url).

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

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

package Foo::C::Too;
use base qw(Foo::C);
sub foo : Handler(foo) { '...' }
sub too { '...' }
sub baz : Handler(abc) { '...' }

Где подпрограммы foo и baz являются обработчиками команд foo и abc или обработчиками url http://www.server.com/foo.html и http://www.server.com/abc.html, а подпрограмма too не является обработчиком.

Модуль Foo::C::Too унаследован от Foo::C, в котором определена MODIFY_CODE_ATTRIBUTES:

package Foo::C;
sub MODIFY_CODE_ATTRIBUTES {
my ($package, $sub, @attr) = @_;
foreach (@attr) {
if (m/^Handler\((.+)\)$/) {
# Регистрация в таблице диспетчеризации
# подпрограммы $sub для команды $1.
# ...
last;
}
}
return ();
}

При запуске ядро просматривает каталог Foo/C/ и загружает все модули Foo::C::*, а perl уже сам, вызывает Foo::C::MODIFY_CODE_ATTRIBUTES для каждой подпрограммы модуля, унаследованного от Foo::C.

Подробности работы с атрибутами смотрите в perldoc attributes.

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

no strict "refs";
foreach (keys %INC) {
if (m/(Foo\/C\/.+)\.pm$/) {
my $p = $1;
$p =~ s/\//::/g;
while (my ($key, $val) = each(%{*{"$p\::"}})) {
if ($key =~ m/^h_(.+)/ and my $sub = *$val{CODE}) {
# Регистрация в таблице диспетчеризации
# подпрограммы $sub для команды $1.
# ...
}
}
}
}

Соответственно в модуле Foo::C::Too, обработчиками являются h_foo и h_baz:

package Foo::C::Too;
sub h_foo { '...' }
sub too { '...' }
sub h_baz { '...' }

На практике вместо префикса "h_" лучше использовать набор префиксов, например: "u_" - пользовательские команды, "a_" - команды администраторов, "o_" - команды доступные всем.

Дополнительные свойства обработчиков можно помещать в our переменные с именами, идентичными именам подпрограмм-обработчиков.

Последний способ: имя обработчика соответствует имени модуля. Этот способ хорошо подходит тогда, когда обработчики являются не просто подпрограммами, а целыми классами. Обычно такие обработчики находятся глубоко в системе и запрятаны за каким-то простым обработчиком.

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

А если вы занимаетесь просто созданием сайтов, то посмотрите готовые фреймворки.

пятница, 4 сентября 2009 г.

О вреде программирования

Каждая профессия накладывает на человека свой специфический отпечаток. Нередко этот отпечаток имеет негативную составляющую.

Специальность программирование взращивает в человеке иллюзию легкого исправления будь чего.
Но в реальной жизни не так просто исправить что-то.

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

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

P.S.
Знание языка Perl лишь усугубляет ситуацию, так как Perl является чрезвычайно гибким языком программирования.

четверг, 27 августа 2009 г.

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

Когда необходимо предотвратить случайное изменение входных данных в плагине, можно либо передать копию данных, воспользовавшись модулем Clone, либо сделать данные неизменяемыми. Для последнего наиболее оптимальным является использование модуля Data::Lock или Hash::Util (идет в стандартной поставке Perl).

четверг, 20 августа 2009 г.

Perl7 за 7 секунд :-)

Сейчас идет разработка Perl6, а используется Perl5:

> perl -e 'print "This is Perl v$]!\n"'
This is Perl v5.008008!

Per6, Perl6 - все хотят Perl6. А я хочу Perl7! :-)

> perl -e '$] = 7; print "This is Perl v$]!\n"'
Modification of a read-only value attempted at -e line 1.

Не вышло... :-(
А если постараться?

> perl -e 'Internals::SvREADONLY($],0); $]=7; print "This is Perl v$]!\n"'
This is Perl v7!

Ура!!! Perl7 существует!!! На радостях пошел купятся в море!!!