Последовательности в Perl 6 / Rakudo

Original author: colomon
  • Translation
В Perl 6 введён новый оператор… для задания последовательностей. Вот как это работает:

my @even-numbers  := 0, 2 ... *;    # арифметическая последовательность
my @odd-numbers   := 1, 3 ... *;
my @powers-of-two := 1, 2, 4 ... *; # геометрическая последовательность

Пример использования:

> my @powers-of-two := 1, 2, 4 ... *; 1;
1
> @powers-of-two[^10]
1 2 4 8 16 32 64 128 256 512


(При задании в REPL геометрической последовательности, которая бесконечна по определению, я поставил в конце строчки «1;». В результате REPL выводит единицу, и не уходит в бесконечный цикл.)

Чтобы ограничить бесконечную «ленивую» последовательность, в примере я использовал запись [^10], что означает «первые десять элементов». При такой записи подсчитанные переменные запоминаются для дальнейшего использования.

Оператор последовательностей … — мощное средство для создания «ленивых» списков. Если ему задать одно число, он начинает отсчёт с него. Если задать два, он расценивает их, как арифметическую последовательность. Если три – он проверяет, не являются ли они началом арифметической или геометрической последовательности – если да, то он продолжает их.

Конечно, многие последовательности не являются арифметическими или геометрическими. Вы можете явно задать функцию, определяющую следующее число в последовательности:

> my @Fibonacci := 0, 1, -> $a, $b { $a + $b } ... *; 1;
1
> @Fibonacci[^10]
0 1 1 2 3 5 8 13 21 34


Часть -> $a, $b { $a + $b } – это стрелочный блок (лямбда-функция), принимающая два аргумента, и возвращающая их сумму. Оператор последовательности вычисляет, сколько аргументов принимает блок, и передаёт нужное количество с конца последовательности для генерации следующего числа.

Пока что у всех примеров в конце была указана звёздочка, означающая «что угодно». В этом случае у списка нет конца. Если вы поставите там число, оно станет окончанием списка.

> 1, 1.1 ... 2
1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2
> 1, 1.1 ... 2.01
... шестерёнки Rakudo вращаются, ибо этот список бесконечен ...
> (1, 1.1 ... 2.01)[^14]
1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2 2.1 2.2 2.3


Первый список оканчивается естественным образом, но второй проходит мимо ограничителя. В результате мы получаем бесконечный список – поэтому я ограничил его 14-ю элементами, просто чтобы увидеть, что он выдаёт.

Программисты, знакомые с вычислениями с плавающей запятой, наверно ворчат, что нельзя предполагать, будто последовательное добавление 0.1 к числу обязательно приведёт к двойке. В Perl 6 используется Rat-математика, которая обеспечивает точность и работоспособность таких вычислений.

Если бы мне надо было найти все числа Фибоначчи меньше 10000, мне бы было сложно вычислить, какой номер будет в последовательности последний. К счастью, кроме блока для задания формулы последовательности мы можем использовать и блок для пограничного условия.

> 0, 1, -> $a, $b { $a + $b } ... -> $a { $a > 10000 };
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946


Стрелочный блок -> $a { $a > 10000 } создаёт проверку. Она принимает один аргумент и возвращает true, когда он становится больше 10000.

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

> 0, 1, -> $a, $b { $a + $b } ...^ -> $a { $a > 10000 };
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765


Используя замыкания «чего угодно», эту запись можно переделать следующим образом:

> 0, 1, * + * ...^ * > 10000;
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765


Яснее такая запись, или сложнее – вам решать.

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

> my @Fibonacci := 0, 1, * + * ... *; 1;
1
> @Fibonacci ...^ * > 10000
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
> @Fibonacci[30]
832040


Кроме этого, оператор последовательности не ограничен работой исключительно с числами. Задавая собственную функцию для подсчёта следующего элемента списка, вы можете составить его из каких угодно элементов.
  • +27
  • 7.4k
  • 7
Support the author
Share post

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 7

    +6
    А если я хочу задать последовательность, где следующий член — произведение трёх предыдущих, мне надо писать * * * * *? :-)
      0
      Ребят, а где сейчас перл актуален?
        +5
        Это не просто Перл, это Перл6 — совсем другой язык. И он нигде не актуален, насколько я понимаю, им занимаются из любви к ковырянию в таких вещах. Перл5 же ещё используется, в основном — по историческим причинам (проект написан на нём или некий коллектив ничего другого знать не желает).
          +1
          Кто знает, может и взлетит. С новыми языками никогда не угадаешь. Хотя всё же выглядит сильно хардкорным для мейнстримового программирования.
        0
        Да, прошли те времена когда я писал SMPP демона на перле, который реализовал казино по СМС. Сейчас всякие руби\питоны да и java начисто вытеснили его из ниши никсовых демонов и скриптовых языков
          0
          А какую ошибку выдаст такой код?
          my @lost-numbers  := 4, 8, 15, 16, 23, 42 ... *; 
          
            0
            my @lost-numbers := 4, 8, 15, 16, 23, 42… *;
            say @lost-numbers[^6]

            4 8 15 16 23 42

            my @lost-numbers := 4, 8, 15, 16, 23, 42… *;
            say @lost-numbers[^10]

            ===SORRY!===
            Unable to deduce arithmetic or geometric sequence from 16,23,42 (or did you really mean '..'?)

            my @lost-numbers := 4, 8, 15, 16, 17… *;
            say @lost-numbers[^10]

            4 8 15 16 17 18 19 20 21 22

          Only users with full accounts can post comments. Log in, please.