Русская Википедия:Raku

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Шаблон:Карточка языка программирования

Raku (от Шаблон:Lang-jaШаблон:Произн — Рай,[1][2] и от Шаблон:Lang-ja2, Шаблон:Произн — счастье, лёгкость, сукха[3][4]) — язык программирования из семейства Perl-подобных языков. Серьёзный пересмотр как дизайна, так и реализации языка Perl, нарушающий обратную совместимость с ним, хотя до 2010 года еще предполагалось наличие режима совместимости.[5]

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

Прежнее название Raku — Perl 6.[6] В течение многих лет в сообществе Perl имели место шуточные замечания о дате релиза. На вопрос «когда выйдет Perl 6» обычным ответом было «на Рождество», но без указания года.[7][8] В 2015 году, то есть после пятнадцати лет ожидания, наконец была анонсирована так называемая «рождественская» версия.[9][10][11]

История

«

В Perl 6 мы решили, что лучше исправить язык, чем исправлять пользователя.

»
— Анонимус

Разработка Perl 6 была впервые анонсирована Ларри Уоллом 19 июля 2000 года, в четвертый день Perl Conference того года,[12] в его выступлении State of the Onion.[13] В то время первоочередными задачами было: удалить из языка «исторические бородавки»; «простые вещи должны оставаться простыми, сложные вещи должны становиться проще, и невозможные вещи должны стать сложными»; общая чистка внутреннего дизайна и API. Процесс начался с серии RFC. Этот процесс был открыт для всех участников, и ни один аспект языка не оставался закрытым для изменений.[14]

Был получен 361 запрос, каждый из которых был рассмотрен Уоллом. Затем он начал процесс написания нескольких «Апокалипсисов» — христианский термин, означающий «раскрытие хороших новостей хорошим людям».[15] Хотя первоначальной целью было написать по одному Апокалипсису для каждой главы книги en:Programming Perl, стало очевидно, что по мере написания каждого Апокалипсиса, предыдущие Апокалипсисы аннулировались более поздними изменениями. По этой причине был опубликован набор Синопсисов, каждый из которых относился к одному Апокалипсису, но включал корректировки из новых Апокалипсисов. Сегодня спецификация Raku управляется набором тестов «roast»,[16] в то время как Синопсисы хранятся в качестве исторической справки.[17]

Есть также серия Экзегез, написанных Дэмиэном Конуэем, которые объясняют содержание каждого Апокалипсиса с точки зрения практического использования. Каждая Экзегеза состоит из примеров кода с обсуждением их использования и значения.[18]

Первоначальные цели и последствия

Основной целью, которую Уолл предложил в своей первоначальной речи, было удаление «исторических бородавок». К ним относилась путаница в сигилах массивов и хэшей, неоднозначность функций select, проблемы с использованием голых[19] (без пунктуации[20]) файловых дескрипторов. Уолл также упомянул в своей речи много других проблем, решение которых Perl-программисты обсуждали годами.Шаблон:Нет АИ

Последствием этих целей стала потеря обратной совместимости. Поскольку обратная совместимость обычно подразумевается при улучшении программного обеспечения, ломающие её изменения в Perl 6 должны были быть явно сформулированы. Постепенно различие между Perl 5 и Perl 6 стало настолько большим, что Шаблон:Date Perl 6 был переименован в Raku.[6]

За прошедшие годы вектор развития Raku менялся несколько раз. Введение концепций из Python и Ruby было ранним влиянием.[21][22][23] Кроме того, Pugs, первый интерпретатор Raku, написан на функциональном языке Haskell, и многие элементы функционального программирования были впитаны командой разработчиков Raku.[24][25]

Маскот

Файл:FOSDEM 2015 Larry Wall and Camelia the Perl6 logo.jpg
Ларри Уолл на фоне бабочки Камелии — талисмана Perl 6

Маскот языка — насекомое Камелия.[26] Его имя — отсылка к эмблеме языка Perl, верблюду («Camel»), а его форма, в традициях любящего каламбуры сообщества Perl, перекликается со словом «bug». Спиральные узоры, вписанные в его крылья, похожие на крылья бабочки, напоминают символы «P6», а расходящееся косоглазие — это преднамеренный каламбур с выражением «Wall-eyed».[27]

Одна из целей живого и красочного дизайна логотипа заключалась в том, чтобы препятствовать мизогинии в сообществе и дать возможность людям с «мужскими убеждениями» показать свою чувствительную сторону.[28]

Реализации

Rakudo — наиболее развитая реализация,[29] что не делает её официальной версией языка, т.к. язык определяется не реализацией, а набором тестов.[30]

Rakudo позволяет выполнять[31] код в виртуальных машинах MoarVM, JVM и на платформе Node.js.[32] MoarVM — виртуальная машина, созданная специально для Rakudo и компилятора NQP.[33][34] Компилятор NQP (Not Quite Perl 6, Шаблон:Tr-en) реализует подмножество языка, включающее правила Raku для разбора исходного кода, работы с абстрактным синтаксическим деревом и бэкенд-специфичной кодогенерацией. Значительная часть Rakudo написана на Raku и NQP. Rakudo — не самодостаточный компилятор, и в данный момент раскрутка компилятора не планируется.

После версии «2015.02», в Rakudo была прекращена поддержка виртуальной машины Parrot[35] (предшественницы MoarVM), создающейся исходя из ошибочного предположения, что Perl 6 будет похож на Perl 5.[36][37]

Исторические реализации

Первой реализацией Raku был Pugs[38] — интерпретатор и компилятор, написанный на Haskell. Был самой продвинутой реализацией, однако с 2007 года вносились только минимальные исправления для соответствия новым версиям GHC, и по состоянию на ноябрь 2014 года Pugs не поддерживается.[39] В августе 2006 года Yichun Zhang разбил файлы тестов Pugs на фрагменты, прикрепив к ним соответствующие параграфы Синапсисов,[40][41] а в январе 2008 года эти тесты были интегрированы в официальные тесты языка («roast»).[42][43] В феврале 2015 года сообщество объявило тесты языка его спецификацией.[44]

  • «Niecza» — компилятор из Perl 6 в байт-код виртуальной машины «Common Language Runtime» (CLR);
  • «Yapsi» — компилятор, написанный на языке Perl 6 реализации «Rakudo».

Основные отличия от Perl

Raku и Perl отличаются фундаментально, хоть в основном и было намерение оставить Raku Perl'овым. Большая часть изменений предназначена для нормализации языка, чтобы его было легче понять как новичкам, так и опытным программистам, и сделать «простые вещи проще, а сложные — более возможными».

Спецификация

Основное нетехническое различие заключается в том, что Raku начинался как спецификация.[30] Это значит, что при необходимости Raku может быть повторно реализован, а также, что программистам не нужно читать исходный код, чтобы получить полный контроль над любой его возможностью. В случае же языка Perl, официальная документация только описывает поведение текущего интерпретатора. Любые расхождения, обнаруженные между документацией и реализацией, могут привести к тому, что либо одно, либо другое будет изменено, что является движущей силой постоянной разработки и совершенствования выпусков Perl.

Система типов

В Raku динамическая система типов была дополнена статическими типами.[45] Например:

my Int $i = 0;
my Rat $r = 3.142;
my Str $s = "Hello, world";

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

my $i = "25" + 10; # $i is 35

Список формальных параметров подпрограмм

В Perl у подпрограмм нет формальных параметров, хоть простая проверка количества и типов параметров возможна с помощью прототипов подпрограмм.[46] Вместо этого, фактические параметры, передаваемые по ссылке, прячутся за элементами массива @_ в теле подпрограммы.

В Raku появляются настоящие формальные параметры.[47] Например:

sub do_something(Str $thing, Int $other) {
    ...
}

Также, как в языке Perl, в Raku параметры передаются по ссылке, но по умолчанию в Raku они константны, т.е. значения в них не могут быть изменены. Они могут быть явно объявлены как позволяющие менять оригинальное значение (is rw) или как копии (is copy), что эквивалентно передаче по значению.

Способы передачи параметров

В Raku три способа передавать параметры: позиционный, именованный и хлюпающий (slurpy).

Позиционные параметры — это обычный упорядоченный список, как в большинстве языков программирования. Даже они могут быть переданы в произвольном порядке, если указывать их имена. Параметры же, которые могут быть переданы только по имени, обозначаются символом : перед сигилом формального параметра. Хлюпающие параметры — это способ создавать вариативные функции в Raku. Они обозначаются символом * перед сигилом формального параметра. Slurpy-хэш будет захватывать неупомянутые при объявлении подпрограммы именованные параметры, а slurpy-массив — последующие позиционные фактические параметры.

В следующем примере присутствуют все три способа, включая slurpy-массив.

sub somefunction($a, $b, :$c, :$d, *@e) {
    ...
}

somefunction(1, 2, :d(3), 4, 5, 6); # $a=1, $b=2, $d=3, @e=(4,5,6)

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

Блоки и замыкания

Параметры также могут быть переданы в любые блоки кода, которые ведут себя как замыкания. В частности, например, тела циклов for и while являются замыканиями. В следующем примере цикл берёт из списка по три элемента за раз и передаёт их в блок как переменные $a, $b и $c.[48]

for @list -> $a, $b, $c {
    ...
}

Это называется «pointy sub» или «pointy block», и стрелка в нём ведёт себя примерно как ключевое слово sub, вводя анонимное замыкание (или анонимную подпрограмму в терминологии Perl).[47]

Инвариантность сигилов

В Perl, сигилы (знаки пунктуации, находящиеся перед именами переменных) меняются в зависимости от вида использования переменной.

# Perl-код
my @array = ('a', 'b', 'c');
my $element = $array[1];    # возвращает 'b',
my @extract = @array[1, 2]; # возвращает ('b', 'c')
my $element = @array[1];    # возвращает 'b' с предупреждением (с версии 5.10)

В Raku же сигилы инвариантны: как у массива, так и у элемента массива будет один и тот же сигил.[45]

# Raku-код
my @array = 'a', 'b', 'c';
my $element = @array[1];    # в $element записывается 'b'
my @extract = @array[1];    # в @extract записывается ('b')
my @extract = @array[1, 2]; # в @extract записывается ('b', 'c')

Изменчивость сигилов в Perl вдохновлена согласованием числа в английском и многих других естественных языках.

"Это яблоко."                    # $a        верно
"Эти яблоки."                    # @a        верно
"Это третье яблоко."             # $a[3]     верно
"Эти третье яблоко."             # @a[3]     не верно

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

# Perl-код: получение массива из элемента хэша.
# В этом хэше хранятся хэши, содержащие массивы.
my @trans_verbs = @{ $dictionary{ 'verb' }{ 'transitive' } };

Подобные конструкции не имеют аналогов в распространённых естественных языках, что вызывает высокую когнитивную нагрузку при написании кода. Тот же код на языке Raku:

# Raku-код: получение массива из элемента хэша.
# В этом хэше хранятся хэши, содержащие массивы.
my @trans_verbs = %dictionary<verb><transitive><>;

Объектно-ориентированное программирование

В языке Perl объектно-ориентированное программирование поддерживается через функцию bless, превращающую любую переменную в объект определенного класса, у которого становятся доступными для вызова методы, объявленные в классе.[49]

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

Raku оставляет низкоуровневый метод bless, но также предоставляет более жестко огранивающую объектную модель для распространенных случаев.[50][51] Например, класс, инкапсулирующий точку в декартовой системе координат, может быть определён следующим образом:

class Point is rw {
    has $.x;
    has $.y;
    
    method distance( Point $p ) {
        sqrt(($!x - $p.x) ** 2 + ($!y - $p.y) ** 2)
    }
    
    method distance-to-center {
        self.distance: Point.new(x => 0, y => 0)
    }
}

my $point = Point.new( x => 1.2, y => -3.7 );
say "Позиция точки: (", $point.x, ', ', $point.y, ')';
# Позиция точки: (1.2, -3.7)

# Изменение x и y (используются методы "x" и "y"):
$point.x = 3;
$point.y = 4;
say "Позиция точки: (", $point.x, ', ', $point.y, ')';
# Позиция точки: (3, 4)

my $other-point = Point.new(x => -5, y => 10);
$point.distance($other-point); #=> 10
$point.distance-to-center;     #=> 5

В Perl методы вызываются с помощью стрелки: $object->method(). В Raku вместо стрелки используется точка, как во многих других языках (например, Java и Python).

В терминологии Raku $.x называется атрибутом. Метод, используемый для доступа к атрибуту называется аксессором[52] (от Шаблон:Lang-en — доступ). Он создаётся автоматически (при объявлении атрибута через точку[53]) и называется так же, как атрибут. Он работает как геттер и, когда класс или атрибут is rw, приобретает свойства сеттера и может использоваться в левой части присваивания. Вместо автоматических аксессоров, программист может определить свои нестандартные методы. Также, в теле класса ко всем атрибутам, вне зависимости от того, как они объявлены, можно обратиться напрямую, используя синтаксис $!.

Наследование, роли и классы

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

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

Роли в Raku берут на себя функцию интерфейсов в Java, примесей в Ruby, трейтов[54] в PHP и Squeak (диалекте языка Smalltalk). Они похожи на классы, но обеспечивают более безопасный, чем наследование, механизм композиции, предотвращающий конфликты имён атрибутов и методов.[55][56] Роли не встраиваются в цепочку наследования. Роли относятся к номинальной системе типов (см. en:Nominal type system), а не структурной, которая применяется, например, в Go. Они предоставляют семантические названия наборов поведений и состояний. Фундаментальное различие между ролями и классами в том, что роли не инстанцируют объекты.[57]

Хоть роли и отличаются от классов, возможно написать код, создающий объект из роли. Попытка использовать роль для создания объекта приведёт к созданию класса с тем же именем. В документации Raku этот механизм назван автоматическим каламбурированием ролей, т.к. сгенерированный класс является каламбуром.[58]

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

Например, собака — это млекопитающее, потому что собаки наследуют у млекопитающих некоторые черты, такие как молочные железы и, через их предков позвоночных, позвоночник. С другой стороны, собаки могут обладать разными типами поведения, которые могут меняться со временем. Например, собака может быть домашней, бродячей, быть поводырём. Эти наборы дополнительных поведений могут быть добавлены к собаке. Можно описать их таким образом, чтобы можно было их применять по отношению к другим животным. Например, кошка может быть домашней и бродячей. Собака и кошка отличаются друг от друга, но остаются в общей категории млекопитающих. Итак, Млекопитающее — это класс, Собака и Кошка — классы, унаследованные от млекопитающего. Но вышеназванные поведения — это роли, которые можно добавить в классы или объекты, созданные из классов.

class Млекопитающее is Позвоночное {
    ...
}
class Собака is Млекопитающее {
    ...
}
role Питомец {
    ...
}
role Бездомный {
    ...
}
role Поводырь {
    ...
}

Роли добавляются к классам и объектам с помощью ключевого слова does. Для наследования же используется ключевое слово is. Эти слова отражают различие в смысле этих возможностей языка: присоединение роли наделяет класс поведением роли, но не означает, что этот класс становится буквально тем же самым, что и эта роль.

class СобакаПоводырь is Собака does Поводырь {
    ...
}   # Подкласс присоединяет роль.

my $собака = Собака.new();
$собака does Поводырь;       # Объект присоединяет роль.

И роли, и классы являются типами. Роль может быть использована в объявлении переменной. Например, роль Незрячий может содержать атрибут типа Поводырь, в котором может быть собака-поводырь, лошадь-поводырь, человек-поводырь или даже машина-поводырь.

class Человек {
    has Собака $собака;     # Может содержать любой вид собаки —
    ...                     # не важно, Поводырь это или нет.
}

role Незрячий {
    has Поводырь $поводырь; # Может содержать любой объект с ролью
    ...                     # Поводырь — не важно, Собака это или нет.
}

Регулярные выражения

Регулярные выражения и обработка строк всегда были одними из определяющих особенностей Perl.[59] Поскольку шаблоны Perl в определенный момент превзошли возможности регулярных выражений,[60] документация Raku называет их просто регексами, дистанцируясь от формального определения регулярных выражений.

Raku расширяет набор функций Perl в отношении регулярных выражений, вкладывая их в бо́льший фреймворк для создания парсеров, называемый правилами.[61] Правила предоставляют возможности контекстно-зависимого разбора (такие как синтаксический предикат из PEG и ANTLR) и ведут себя как замыкания относительно своих лексических областей видимости.[62] Правила вводятся с помощью ключевого слова rule, использование которого похоже на определение подпрограммы. Анонимные же правила могут быть введены с помощью ключевого слова regex (или rx), или их можно описывать как регулярные выражения в Perl — с помощью операторов m (сопоставление) или s (замена).

В Апокалипсисе 5 Ларри Уолл перечисляет 20 проблем текущей «культуры использования регулярных выражений». В числе прочего, регулярные выражения Perl были «слишком компактными и „милыми“», они «слишком надеялись на слишком небольшой набор специальных символов», у них была «слабая поддержка именованного захвата», «слабая поддержка грамматик» и «плохая интеграция с языком».[63]

Синтаксические упрощения

Некоторые конструкции Perl в Raku были изменены и оптимизированы для других синтаксических оборотов. Например, круглые скобки, которые были обязательны в инструкциях порядка выполнения, теперь опциональны.[48]

if is-true() {
    for @array {
        ...
    }
}

Оператор , (запятая) теперь является конструктором списков, поэтому скобки вокруг списков не требуются.

@array = 1, 2, 3, 4;

Цепочки сравнений

Raku разрешает следующие выражения:

if 20 <= $temperature <= 25 {
    say "Температура в комнате — от 20 до 25 градусов!"
}

Это воспринимается как последовательные сравнения слева направо с последующим объединением через логическое И.

Ленивые вычисления

Raku использует технику ленивых вычислений списков подобно некоторым функциональным языкам, таким как Haskell:[64]

@integers = 0..Inf; # целые числа от нуля до бесконечности

Этот код не выдаст ошибку, пытаясь поместить бесконечный список в массив @integers, и не зависнет, пытаясь бесконечно увеличивать список, если запрашиваться будет конечное число элементов.

Это упрощает многие распространенные в Raku задачи, в том числе операции ввода-вывода, трансформации списков и передачу параметров.

Возможно также создание ленивых списков с помощью ключевых слов gather и take. Они похожи на генераторы в языках Icon и Python.

my $squares = lazy gather for 0..Inf {
    take $_ * $_;
};

Здесь $squares — это бесконечный список квадратов натуральных чисел (включая ноль), но благодаря ленивой природе этого списка, элементы вычисляются только тогда, когда к ним происходит доступ.[65]

Скрещения

Raku вводит концепцию скрещений[66] (Шаблон:Lang-en — соединение, место пересечения; в Raku этот термин имеет также отношение к конъюнкции и дизъюнкции[64]). Это суперпозиция нескольких значений.[64] В простейшем виде, скрещение создаётся операторами скрещения:

# Пример скрещения типа | ("any"):
my $color = 'белый';
unless $color eq 'белый' | 'чёрный' | 'серый' {
    die "Печать этим цветом не поддерживается.\n";
}

# Пример скрещения типа & ("all"):
my $password = 'secret!123';
if $password ~~ /<:alpha>/ & /<:digit>/ & /<:punct>/ {
    say "Ваш пароль достаточно надёжен.";
}

Оператор | выражает значение равное либо левому, либо правому аргументу, оператор & — одновременно и левому, и правому. Эти значения могут быть использованы в коде везде, где по смыслу предполагается одно значение. Любые операции со скрещением действуют одновременно на все его составляющие, и результат объединяется через оператор этого скрещения. Так, ("apple"|"banana") ~ "s" вернёт "apples"|"bananas". Однако в булевом контексте скрещения возвращают лишь одно значение — истину или ложь: any возвращает истину, если сравнение истинно для хотя бы одного элемента; all возвращает истину, если сравнение истинно для всех элементов.[67]

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

subset Color of Any where RGB_Color | CMYK_Color;
sub get_tint(Color $color, Num $opacity) {
    ...
}

Скрещения — это особые объекты, разделяющие выполнение кода на потенциально-параллельные потоки. И они созданы специально для применения в булевом контексте: нельзя получить доступ к их содержимому непосредственно, без конвертации в строку, что отличает их, например, от множеств и других коллекций.[67]

Макросы

В низкоуровневых языках, макросы стали синонимом текстовой замены в исходном коде из-за ассоциаций с препроцессором языка Си. Однако в высокоуровневых языках, таких как Лисп, который появился раньше Си, макросы были более мощными.[68] Этой лиспоподобной концепцией макросов Raku и воспользовался.[47] Мощность этого типа макросов базируется на оперировании программой как высокоуровневой структурой данных, а не текстом, и на доступе ко всем возможностям языка.

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

macro hello($what) {
    quasi { say "Hello { {{{$what}}} }" };
}

В примере выше, парсинг аргумента макроса происходит до исполнения макроса, что ведёт к более информативным диагностическим сообщениям компилятора. Тем не менее, поскольку исполнение тела макроса происходит во время компиляции (для каждого случая использования), могут быть применены самые разные техники оптимизации. С помощью макросов возможно даже произвести большую часть работы некоторых программ до начала их выполнения.

Идентификаторы

В Raku идентификаторы, помимо букв, цифр и подчеркиваний, могут также содержать апострофы и дефисы, при условии, что после них обязательно идут буквы. Буквы включают «соответствующие» (какие — зависит от реализации) символы Юникода, которые в Rakudo и MoarVM определены как все символы Юникода категории «L».[69]

Использование дефисов вместо подчеркиваний называется «kebab case».[70][71][72]

Метаоператоры

Метаоператоры — операторы, параметризующиеся другими операторами, подобно тому, как функции могут принимать другие функции в качестве параметров.[73]

Метаоператор присваивания

Perl унаследовал такие операторы языка Си, как +=, *= и т.п. Raku обобщает их до метаоператора. Для любого бинарного оператора op мы можем написать:

$x op= $y;  # или $x [op]= $y

Что значит:

$x = $x op $y;

Причем, это работает и для операторов, определённых пользователем.

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

Они похожи на оператор map в Perl. Заставляют операторы работать со всеми значениями массива. Могут применяться как к бинарным, так и к унарным операторам.[74]

Например, следующий код создаст массив, содержащий все элементы массива @a, увеличенные на единицу:

my @aPlusOne = @a »+» 1;    # или @a >>+>> 1

Направление угловых скобок влияет на поведение при передаче в качестве параметров массивов разной длины.[74] Эти «стрелки» показывают, откуда берётся длина результата операции.

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

my @a = 1, 2, -3;
my @b = -<<@a;      # [-1 -2 3]

Гипероператоры работают не только для плоских, но и для вложенных массивов.[75]

Метаоператор редукции

Метаоператор редукции может использоваться с любым инфиксным оператором, преобразуя его в оператор свёртки списка. Это похоже на то, как если бы оператор был применён к первым двум элементам, затем к получившемуся значению и третьему элементу и т.д., пока не останется только одно значение. В качестве оператора-параметра может выступать сумма, произведение, максимум, минимум и т.д. Это чрезвычайно мощный механизм, который, например, переводит оператор + в оператор суммы списка, как в примере ниже.

say "Сумма целых чисел от 0 до 99 равна: ", [+] ^100;

Кросс-оператор

Ведёт себя одновременно и как оператор, и как метаоператор.[76]

Кросс-оператор[74] находит прямое произведение списков, упорядоченное таким образом, что перечисление элементов из правого операнда происходит быстрее, чем из левого,[76] возвращая последовательность списков:

1..3 X <a b c> X <d e f>;
# ((1 a d) (1 a e) (1 a f) 
#  (1 b d) (1 b e) (1 b f)
#  (1 c d) (1 c e) (1 c f)
#  (2 a d) (2 a e) (2 a f)
#  (2 b d) (2 b e) (2 b f)
#  (2 c d) (2 c e) (2 c f)
#  (3 a d) (3 a e) (3 a f)
#  (3 b d) (3 b e) (3 b f)
#  (3 c d) (3 c e) (3 c f))

Метаоператор сворачивает внутренние списки с помощью оператора-параметра:[76]

1..3 X~ <a b c> X~ <d e f>;
# (1ad 1ae 1af 1bd 1be 1bf 1cd 1ce 1cf
#  2ad 2ae 2af 2bd 2be 2bf 2cd 2ce 2cf
#  3ad 3ae 3af 3bd 3be 3bf 3cd 3ce 3cf)

Зип-оператор

От Шаблон:Lang-en — застёжка-молния.[77]

Аналогично кросс-оператору, совмещает элементы списков,[74] но возвращает последовательность, содержащую сначала первые элементы каждого списка, затем вторые элементы каждого списка и т.д.[78]

<a b c> Z <1 2 3 4>;
# ((a 1) (b 2) (c 3))

100, 200 Z+ 42, 23;
# (142 223)

Обратные операторы

Метаоператор R (Шаблон:Lang-en) позволяет менять местами аргументы исходного оператора.

say "Один делить на три равно ", 3 R/ 1;

Вложенность метаоператоров

Результатом применения метаоператора к оператору является другой оператор, к которому снова может быть применен метаоператор и т.д. Для устранения неоднозначности в синтаксисе разрешено использовать квадратные скобки.[79]

my @a = 1, 2, 3;
my @b = 5, 6, 7;

@a >>>>> @b;        # Ошибка разбора.
@a >>[>]>> @b;      # [False False False]
# Здесь гипероператор >> >> применяется к оператору сравнения.

# Кросс-метаоператор применяется
# к метаоператору присваивания, параметризованному
# оператором сложения:
@a X[+=] @b;        # (6 12 19 7 13 20 8 14 21)

# Из-за кросс-метаоператора, присваивание делалось
# для каждого элемента массива @a с каждым элементом массива @b,
# что эквивалентно прибавлению к каждому элементу массива @a
# суммы элементов массива @b:
say [+] @b;         # 18
say @a;             # [19 20 21]

Конкурентность и параллелизм

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

Raku предоставляет простой модульный высокоуровневый API для написания конкурентного кода, независящий от способов реализации этого API виртуальной машиной. Кроме того, некоторые функции языка могут неявно работать асинхронно. Чтобы обеспечить управляемость и совместимость между этими функциями, пользовательскому коду следует избегать, насколько это возможно, использования низкоуровневых интерфейсов (потоков, планировщиков, блокировок).[80]

Центральным высокоуровневым механизмом являются промисы[81][82] (Шаблон:Lang-en — обещание), представляющие собой результаты вычислений, полученные до их фактического завершения. Их можно давать, выполнять и нарушать. Таким образом, они обладают тремя возможными состояниями. Сила этого механизма заключается в возможности их комбинировать и соединять в цепочки:

my $p1 = Promise.new();
my $p2 = $p1.then({ say "Результат второго промиса."});

Здесь then обеспечивает выполнение $p2 лишь после выполнения $p1.

Есть также Шаблон:Lang-en («запасы») и Шаблон:Lang-en («поставщики») — механизм создания асинхронных потоков данных с возможностью обработки каждого сообщения сразу несколькими получателями, подобно тому, как работают обработчики событий в других языках программирования.[80] Этот механизм можно использовать для событийно-ориентированного программирования.

Наконец, каналы (Шаблон:Lang-en) — это потокобезопасные FIFO-очереди, подобные именованным конвейерам в операционных системах, но функционирующие в рамках текущего процесса. Ключевое отличие от Supply в том, что чтение из канала является удалением из очереди,[80] т.е. одно значение может быть прочитано лишь единожды.

Примеры

Hello world

Программа «Здравствуй, мир!» часто используется для демонстрации базового синтаксиса языка. На языке Raku она выглядит так:

say 'Hello, world';

Хотя, конечно, есть больше одного способа выразить это.[83]

Факториал

Вычисление факториала, определённое несколькими способами:

# С использованием рекурсии с конструкцией «if-else».
sub fact( UInt $n --> UInt ) {
    if $n == 0 { 1 }
    else       { $n * fact($n-1) }
}

# С использованием рекурсии с «if»
# в качестве модификатора выражения.
sub fact( UInt $n --> UInt ) {
    return 1 if $n == 0;
    return $n * fact($n-1);
}

# С использованием рекурсии с конструкцией «when».
sub fact( UInt $n --> UInt ) {
    when $n == 0 { 1 }
    default      { $n * fact($n-1) }
}

# С использованием тернарного оператора.
sub fact( UInt $n --> UInt ) {
    $n == 0 ?? 1 !! $n * fact($n-1)
}

# С использованием множественной диспетчеризации.
multi fact(0) { 1 }
multi fact( UInt $n --> UInt ) {
    $n * fact($n - 1)
}

# С использованием метаоператора редукции.
sub fact( UInt $n --> UInt ) {
    [*] 1..$n
}

# Определение оператора факториала,
# реализованного через метаоператор редукции.
sub postfix:<!>( UInt $n --> UInt ) { [*] 1..$n }

# С использованием ключевого слова «state» для мемоизации.
sub fact( UInt $n --> UInt ) {
    state %known = 0 => 1;
    return %known{$n} if %known{$n}:exists;
    %known{$n} = $n * fact($n-1);
    return %known{$n};
}

QuickSort

QuickSort — известный алгоритм сортировки. Его реализация, использующая функциональную парадигму, может быть лаконично записанаШаблон:Efn так:

# Отсортированный пустой список — это пустой список.
multi quicksort([]) { () }

# Иначе, берём первый элемент в качестве опорного...
multi quicksort([$pivot, *@rest]) {
    # Делим элементы на список тех,
    # которые меньше опорного, и те,
    # которые больше опорного.
    my @before = @rest.grep(* before $pivot);
    my @after  = @rest.grep(* after $pivot);

    # Сортируем эти подсписки и объединяем результат.
    flat (quicksort(@before), $pivot, quicksort(@after))
}

Шаблон:Notelist

Ханойская башня

Эта головоломка часто используется для знакомства с рекурсией в информатике. Данная реализация использует мультиметоды с литералом 0 как часть сигнатуры.

multi sub hanoi(0, $, $, $) { }                         # Нет дисков. Нечего перекладывать.
multi sub hanoi($n, $a = 'A', $b = 'B', $c = 'C') {     # $n дисков и три стержня: A, B, C.
    hanoi $n - 1, $a, $c, $b;                           # Переместить ($n - 1) дисков с A на B
    say "Переместить диск $n с $a на $c.";              # Переместить последний диск с A на C.
    hanoi $n - 1, $b, $a, $c;                           # Переместить ($n - 1) дисков с B на C.
}

hanoi(5);   # Решение для пяти дисков.

См. также

Примечания

Шаблон:Примечания

Литература

Книги, опубликованные до релиза языка (до версии 6.c)

Ссылки

Шаблон:Perl

  1. Шаблон:Cite web
  2. Шаблон:Cite web
  3. Шаблон:Cite web
  4. Шаблон:Cite web
  5. Шаблон:Cite web
  6. 6,0 6,1 Шаблон:Cite web
  7. Шаблон:Cite web
  8. Шаблон:Cite web
  9. Шаблон:Cite web
  10. Шаблон:Cite web
  11. Christmas is here Шаблон:Wayback.
  12. Шаблон:Cite web
  13. Шаблон:Cite web
  14. Шаблон:Cite web
  15. Шаблон:Cite web
  16. Шаблон:Cite web
  17. Шаблон:Cite web
  18. Шаблон:Cite web
  19. Шаблон:Cite web
  20. Шаблон:Cite web
  21. Шаблон:Cite web
  22. Шаблон:Cite web
  23. Шаблон:Cite web
  24. Шаблон:Cite web
  25. Шаблон:Cite web
  26. Шаблон:Cite web
  27. Шаблон:Cite web (см. код страницы)
  28. Шаблон:Cite web
  29. Download Raku Шаблон:Wayback: «Rakudo is currently the most actively developed and mature implementation of Raku».
  30. 30,0 30,1 Шаблон:Cite web
  31. GitHub Issues: compile to jar files / moarvm files Шаблон:Wayback — обсуждение возможности распространения скомпилированного байт-кода.
  32. Шаблон:Cite web
  33. Шаблон:Cite web
  34. Шаблон:Cite web
  35. Development Release #85 (“Berlin”) Шаблон:Wayback: «this is the last release of Rakudo that supports Parrot as a backend for the foreseeable future».
  36. YAPC::Europe 2013 “Future Perl”: MoarVM: a metamodel-focused runtime for NQP and Rakudo By Jonathan Worthington (‎jnthn‎) Шаблон:Wayback: «We know what we need now» (шестой слайд)
  37. YAPC::Europe 2013 “Future Perl”: MoarVM: a metamodel-focused runtime for NQP and Rakudo By Jonathan Worthington (‎jnthn‎) Шаблон:Wayback — страница выступления на сайте конференции с видео
  38. Pugs — Perl 6 user's golfing system.
  39. Шаблон:Cite web
  40. Шаблон:Cite web
  41. Шаблон:Cite web
  42. Шаблон:Cite web
  43. Шаблон:Cite web
  44. Шаблон:Cite web
  45. 45,0 45,1 Шаблон:Cite web
  46. Шаблон:Cite web
  47. 47,0 47,1 47,2 Шаблон:Cite web
  48. 48,0 48,1 Шаблон:Cite web
  49. Шаблон:Cite web
  50. Шаблон:Cite web
  51. Шаблон:Cite web
  52. Шаблон:Cite web
  53. Шаблон:Cite web
  54. Шаблон:Cite web
  55. Шаблон:Cite web
  56. Шаблон:Cite web
  57. Шаблон:Cite web
  58. Шаблон:Cite web
  59. Шаблон:Cite web
  60. Шаблон:Cite web
  61. Шаблон:Cite web
  62. Шаблон:Cite web
  63. Шаблон:Cite web
  64. 64,0 64,1 64,2 Шаблон:Cite web
  65. Шаблон:Cite web
  66. Шаблон:Cite web
  67. 67,0 67,1 Шаблон:Cite web
  68. Шаблон:Cite book Шаблон:Cite web
  69. Шаблон:Cite web
  70. Шаблон:Cite web
  71. Шаблон:Cite web
  72. Шаблон:Cite web
  73. Шаблон:Cite web
  74. 74,0 74,1 74,2 74,3 Шаблон:Cite web
  75. Шаблон:Cite web
  76. 76,0 76,1 76,2 Шаблон:Cite web
  77. Шаблон:Cite web
  78. Шаблон:Cite web
  79. Шаблон:Cite web
  80. 80,0 80,1 80,2 Шаблон:Cite web
  81. Шаблон:Cite web
  82. Шаблон:Cite web
  83. Шаблон:Cite web
  84. Шаблон:Cite web
  85. Шаблон:Cite web