четверг, 15 января 2009 г.

Гипер-, редакшн- и кроссоператоры в Perl6

Perl насквозь пронизан магией. Возможно, из-за этого адепты языков программирования, лишенных волшебных свойств, так ненавидят его. Они просто боятся и не умеют обращаться с магией. Хотя, что тут уметь! Магия, как и все, подчиняется простым законам природы.

Еще в 1748 году М. В. Ломоносов открыл закон сохранения магии. Или все-таки вещества? - да какая разница! Этот закон гласит: "Так, ежели где убудет несколько магии, то умножится в другом месте".

В соответствии с законом сохранения магии в Perl6, когда многие вещи стали простыми, появились очень интересные новые возможности. Среди нововведений - гипероператоры, reduction и cross операторы. Это операторы предназначены для работы со списками. Кстати, подобные им операторы изначально имеются в магических языках семейства APL.

Наличие таких нововведений в Perl6 позволяет выражать мысли более компактно и ясно. Рассмотрим некоторые примеры и сравним их с примерами на языке J (http://jsoftware.com), который является потомком языка APL. Для простоты в нижеприведенных примерах код обозначен отступом вправо, а результат работы - отступом влево.

Примеры выполнены при помощи Rakudo (компилятор Perl 6, основанный на Parrot) Rakudo - сокращение от японского Rakuda-do, что означает "Путь верблюда".

Перед основыми примерами создаем списки (массивы, вектора):

Perl | J
------------------------------------------------
@x = (1 .. 3) | x=. 1 2 3
@y = (4 .. 6) | y=. 4 5 6

В J списки создаем вручную, чтобы не забегать вперед.


Гипероператоры

Гипероператоры производят операции над списками поэлементно. Смотрите http://perlcabal.org/syn/S03.html#Hyper_operators. Сложение двух списков при помощи гипероператора и прибавление к списку числа:

Perl | J
------------------------------------------------
@x >>+<< @y | x + y
5, 7, 9 | 5 7 9
@x >>+>> 3 | x + 3
4, 5, 6 | 4 5 6

В J оператор является аналогом Perl гипероператора, что не удивительно, ведь APL - язык векторных вычислений.


Reduction операторы

Эти операторы работают не с единичными элементами списка, а с учетом результата действий над предыдущими элементами. http://perlcabal.org/syn/S03.html#Reduction_operators

Perl | J
------------------------------------------------
[+] @x | +/ x
6 | 6

Для достижения аналогичного результата в J оператор модифицируется наречием "поэлементно".

Следующий пример еще не работает в Rakudo (parrot revision 35576). Отличие этого примера от предыдущего в том, что сумма считаеться не для всего списка, а для каждой его части:

Perl | J
------------------------------------------------
[\+] @x | +/\ x
1, 3, 6 | 1 3 6

В J это делается при помощи наречия "префиксно".


Cross операторы

Эти операторы производят действие над каждой комбинацией элементов списков.
http://perlcabal.org/syn/S03.html#Cross_operators

Сделаем фрагмент таблицы умножения:

Perl | J
------------------------------------------------
@x X*X @y | x */ y
4, 5, 6, 8, 10, 12, 12, 15, 18 | 4 5 6
| 8 10 12
| 12 15 18

В J оператор модифицируется наречием "таблично". Отличие Perl от J в том, что в J сразу создается матрица, а Perl придется список преобразовать в таблицу. Просто у Perl и J разное распределение магии.

Выводы

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

Для "профессиональных" векторных вычислений рекомендуется использовать Perl Data Language (http://pdl.perl.org).