49. Как устанавливать модули в Perl 6

В комплект Rakudo Star входит утилита — менеджер модулей zef. (Обратите внимание, что нужен именно Rakudo Star, а не просто компилятор Rakudo Perl 6 — в последнем случае и саму утилиту надо будет установить отдельно.)

Интерфейс довольно простой.

Установка модуля

$ zef install YAML

Далее все происходит автоматически:

===> Searching for: YAML
===> Searching for missing dependencies: TestML
===> Testing: TestML
=== A Basic TestML File ===
=== TestML with no data section ===
=== The Simplest TestML Test That Could Possibly Fail! ===
===> Testing [OK] for TestML
===> Testing: YAML
=== Test Dumping Perl 6 Data Objects to YAML ===
=== Failing Tests For People to Hack On ===
=== Various String Quoting ===
===> Testing [OK] for YAML
===> Installing: TestML
===> Installing: YAML

Удаление модуля

$ zef uninstall YAML

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

Информация о модуле

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

- Info for: YAML
- Identity: YAML
- Recommended By: Zef::Repository::LocalCache
Author:	 Ingy döt Net <ingy@cpan.org>
Description:	 YAML Ain’t Markup Language
License:	 Artistic-2.0
Source-url:	 git://github.com/perl6-community-modules/yaml-pm6.git
Provides: 2 modules
Depends: 1 items

Другие команды

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

48. Sleep Sort на Perl 6

Да, как же без алгоритма Sleep Sort?! Он должен быть аналогом «Hello, World!» для языков, умеющих делать параллельные вычисления. Итак, Sleep Sort на Perl 6.

my @data = 12, 4, 7, 5, 8, 1, 2;
await gather for @data -> $d {
    take start {
        sleep $d / 10;
        say $d;
    }
}

На каждый элемент данных создается отдельный поток, который ждет $d / 10 секунд и печатает число. Можно попробовать поделить и на большее число, но при этом возможно состояние гонки между потоками.

Сочетание await, gather и take позволяет дождаться все потоки и только потом завершить основную программу.

Запускаем программу, она печатает отсортированные числа:

$ perl6 sleep-sort.pl 
1
2
4
5
7
8
12

47. Как отсортировать хеш в Perl 6

Для сортировки хеша в Perl 6 желательно знать, что такое placeholder variable. Как только эта концепция становится понятна, писать условие сортировки становится крайне просто.

Например, создадим хеш с расстояниями до разных городов от Москвы:

my %distance = 
    Владимир    => 185,
    Волгоград   => 1000,
    Калининград => 1227,
    Мурманск    => 1895,
    Новосибирск => 3550;

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

say %distance.sort({$^a.value <=> $^b.value});

В общем-то, готово. Программа печатает то что надо:

(Владимир => 185 Волгоград => 1000 Калининград => 1227 
Архангельск => 1261 Астрахань => 1411 Мурманск => 1895 
Новосибирск => 3550)

Давайте разберемся, что происходит внутри блока, переданного в метод sort.

Во-первых, переменные $^a и $^b — это плейсхолдеры, которые являются аргументами анонимного блока и сортируются по алфавиту. То есть с тем же успехом их можно было бы назвать $^x и $^y или $^var1 и $^var2.

Во-вторых, внутри этих переменных оказываются объекты типа Pair, который содержит поля key и value:

say %distance.sort({
    say $^a.key ~ ', ' ~ $^a.value ~ ' vs. ' ~ 
        $^b.key ~ ', ' ~ $^b.value;
    $^a.value <=> $^b.value
});

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

Владимир, 185 vs. Новосибирск, 3550
Калининград, 1227 vs. Волгоград, 1000
Мурманск, 1895 vs. Астрахань, 1411
Владимир, 185 vs. Волгоград, 1000
Новосибирск, 3550 vs. Волгоград, 1000
Новосибирск, 3550 vs. Калининград, 1227
Астрахань, 1411 vs. Архангельск, 1261
Владимир, 185 vs. Архангельск, 1261
Волгоград, 1000 vs. Архангельск, 1261
Калининград, 1227 vs. Архангельск, 1261
Новосибирск, 3550 vs. Архангельск, 1261
Новосибирск, 3550 vs. Астрахань, 1411
Новосибирск, 3550 vs. Мурманск, 1895

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

say %distance.sort( -> $город1, $город2 {
    $город1.value <=> $город2.value
});

46. Цитирующие скобки в Perl 6

С помощью угловых цитирующих скобок в Perl 6 очень удобно создавать массивы со строками. В Perl 5 похожий механизм давало слово qw.

my @names = <alpha beta gamma>;
.say for @names;

Вы должны сами определиться с тем, ставить ли пробелы после открывающей и перед закрывающей скобкой. Часто можно видеть вот такой вариант:

my @names = < alpha beta gamma >;

В этом случае в массив попадут ровно те же три элемента.

Perl 6 считает разделителем между отдельными элементами все пробельные символы (whitespace), поэтому данные могут содержать, например, дефисы или точки:

my @nums = < . - 10 20.30 -40 >;

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

my @nums = < . - 10 20.30 -40 >;
for @nums {
    say "{$_.^name} $_";
}

Программа печатает следующее:

Str .
Str -
IntStr 10
RatStr 20.30
IntStr -40

Комментарии (ни обычные, ни embedded) внутри цитирующих скобок не работают. Возьмем программу:

my @data = <
    # comment?
    data_load 
>;
.say for @data;

В массиве окажется три элемента:

#
comment?
data_load

45. Численные методы и Perl 6

Вчера в группе Perl 6 на Фейсбуке Solomon Foster показал вот такой пример:

my @x = FatRat.new(1, 1), 
        -> $x { $x - ($x ** 2 - $N) / (2 * $x) } ... *;

В этом коде в одну строку реализован численный метод вычисления квадратного корня (методом Ньютона).

Давайте возьмем какое-то понятное число $N и посмотрим значение его корня на десятой итерации:

10.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000114617269858815597676188906948935679812072077815172636665039841545242611088386175405939458945274038752495996921868425893616324912131911758516481924280284021811361729487444582731688466773998426538733 . . .

Я показал только часть результата, в реальности цифр еще пять раз по столько же.

Что здесь примечательного?

Во-первых, тип FatRat — это дроби с произвольно большими числителем и знаменателем. Если использовать Rat, то мы довольно быстро придем к обычным числам с плавающей точкой.

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

44. Транспонирование матрицы в Perl 6

В Perl 6 есть метаоператор Z, который объединяет два списка как застежка на молнии, по очереди перемежая элементы. Мы, возможно, рассмотрим его как-нибудь отдельно, но сегодня я хочу показать, как этот оператор магически транспонирует матрицу.

Берем двумерную матрицу:

my @matrixA = [1, 2],
              [3, 4];

Если хочется, можно поставить еще одни скобки:

my @matrixA = [[1, 2],
               [3, 4]];

И теперь в одно действие транспонируем:

my @matrixB = [Z] @matrixA;

Метаоператор Z здесь поставлен внутрь оператора редукции. Вуаля, матрица транспонировалась:

[(1 3) (2 4)]

43. Массивы как аргументы функций в Perl 6

В Perl 6 передавать массивы функциям одно удовольствие. Достаточно объявить массив в сигнатуре, и Perl поймет, что с этим делать дальше. В том числе, если дальше идут, например, скаляры.

sub f($a, @b, $c) {
    say "a = $a";
    say "b = @b[]";
    say "c = $c";
}

my @arr = <5 7 9>;
f(10, @arr, 20);

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

a = 10
b = 5 7 9
c = 20

Очень удобно, если сравнивать с Perl 5.

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

sub g(*@data) {
    say @data;
}

g(3, 5, 7);

Эта программа работает правильно:

[3 5 7]

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

===SORRY!=== Error while compiling sub-slurpy.pl
Calling g(Int, Int, Int) will never work with declared signature (@data)
at sub-slurpy.pl:5
------> <BOL>⏏g(3, 5, 7);

42. Фейзеры ENTER и LEAVE в Perl 6

Блоки, которые помечаются большими буквами, в Perl 6 называются фейзерами (phasers). Вы знакомы с ними по Perl 5, например: BEGIN и END.

Сегодня мы рассмотрим два фейзера, которые выполняются при входе в подпрограмму и при выходе из нее: ENTER и LEAVE. В следующем примере это наглядно видно:

sub f() {
   ENTER say 'Hi';
   LEAVE say 'Bye';

   say 'Body';
}

f;

Программа печатает такие строки:

Hi
Body
Bye

То есть сначала выполняется блок ENTER, затем тело функции и наконец блок LEAVE.

Порядок выполнения фейзеров не зависит от того, где они расположены. Например, все работает даже после return:

sub f() {
    say 'Body';
    return;

    LEAVE say 'Bye';
    ENTER say 'Hi';
}

При наличии более одного фейзера того же типа, блоки ENTER выполняются в порядке объявления, а LEAVE в противоположном:

sub f() {
    say 'Body';

    ENTER say 'Hi 1';
    ENTER say 'Hi 2';

    LEAVE say 'Bye 1';
    LEAVE {
        say 'Bye 2';
    }
}

f;

(Здесь одновременно показан пример с блоком кода в одном из фейзеров.)

Программа печатает строки в следующем порядке:

Hi 1
Hi 2
Body
Bye 2
Bye 1