35. Минимум и максимум в Perl 6

В Perl 6 существуют операторы min и max для поиска минимума и максимума. Все очень просто и интуитивно:

say 5 min 10; # 5
say 5 max 10; # 10

Чуть менее очевидно, что такие операторы легко объединяются в цепочку и находить минимальный элемент в более длинных списках:

say 4 min 2 min 10 min 5 min 3; # 2
say 4 max 2 max 10 max 5 max 3; # 10

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

say [min] 4, 2, 10, 5, 3; # 2
say [max] 4, 2, 10, 5, 3; # 10

Наконец, если есть список как объект, то на нем можно вызвать одноименные методы:

say (4, 2, 10, 5, 3).min; # 2
say (4, 2, 10, 5, 3).max; # 10

Это работает и со строками, но, разумеется, они сортируются как строки, независимо от смысла:

say <one two three four five six>.min; # five
say <one two three four five six>.max; # two

34. Оператор последовательности … в Perl 6

В Perl 6 существует оператор, создающий последовательности (sequence operator):

...

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

Итак, рассмотрим основные варианты применения оператора из трех точек.

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

.say for 1...5;

Программа ожидаемым образом печатает числа от одного до пяти. В этом примере тот же эффект был бы достигнут и с помощью диапазона:

.say for 1...5;

Однако, есть несколько отличий. Во-первых, тип созданного объекта:

(1...5).WHAT.say; # (Seq)
(1..5).WHAT.say;  # (Range)

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

.say for 1, 3 ... 11;    # 1 3 5 7 9 11

.say for 1, 2, 4 ... 64; # 1 2 4 8 16 32 64

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

.say for 1, 3 ... 10; # 1 3 5 7 9

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

(1...*).is-lazy.say; # True

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

for 1, 2, 4 ... * -> $n {
    last if $n > 1000;
    say $n;
}

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

.say for 1, {$_ * 3} ... 243;

Эта программа печатает числа 1, 3, 9, 27, 81 и 243. Обратите внимание, что при таком подходе верхняя граница должна быть одним из вычисленных элементов последовательности. Если этого не соблюсти и поставить, например, произвольное большое число, то генератор последовательности проскочит его и продолжит бесконечно генерировать числа.

Вместо блока кода удобно воспользоваться звездочкой:

.say for 1, -* ... *; # 1 -1 1 -1 1 -1 1 -1 . . .

Ознакомьтесь также с заметкой «Цепочки последовательностей».

33. Инкремент строк в Perl 6

В целом заголовок противоречивый, но в Perl 6 операция инкремента и декремента вполне применима и к строкам:

my $s = 'World';

$s++;
say $s; # Worle

$s--;
say $s; # World

Если в строке были цифры, то начинается магия, и увеличивается именно число:

my $n = 'n48';
say $n.WHAT; # Str

say ++$n; # n49
say ++$n; # n50
say ++$n; # n51

При этом новые разряды не добавляются, и в нашем примере при переполнении увеличивается предыдущая буква:

my $n = 'n98';

say ++$n; # n99
say ++$n; # o00
say ++$n; # o01

Наконец, еще она хитрая приятность. Если строка похожа на имя файла, то Perl 6 проявит сообразительность и попытается изменить имя, но не расширение файла. Это удобно применять при создании множества нумерованных файлов:

my $filename = 'data000.csv';
say $filename++ for 1..5;

Получается именно то, что ожидается интуитивно:

data000.csv
data001.csv
data002.csv
data003.csv
data004.csv

P. S. Инкремент строк работает и в Perl 5, но имена файлов там изменить не получится: все сломается и получится 1. Мало того, попытка декремента строки превратит ее в –1.

32. Выбор случайного элемента в Perl 6

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

Это решается крайне просто: в Perl 6 определены методы pick и roll, которые выберут и вернут случайный элемент:

my @a = 'a' .. 'z';
say @a.pick; # b
say @a.roll; # u

Усложняем задачу: выбрать несколько случайных элементов.

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

my @a = 'a' .. 'z';
say @a.pick(5); # (b i c x v)
say @a.roll(5); # (c k m c f)

Уже на этом случайном результате видно, что roll вернул повторяющиеся элементы. Именно так и есть: pick заботится об уникальности возвращаемых данных, а roll — нет.

Из этого свойства вытекает важное ограничение: если запрошенный список длиннее оригинального, то метод pick вернет меньше запрошенного — возвращаемый список будет случайно пересортированным оригинальным.

my @b = 'a' .. 'd';

say @b.pick(10); # (c a b d)
say @b.roll(10); # (a c a c c a b a b b)

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

my @a = 'a' .. 'z';
say pick(3, @a); # (g v d)
say roll(3, @a); # (j w r)