Выпуск 27. Май 2015
← Отладка приложений на AnyEvent | Содержание | Метаоператоры в Perl 6 →Операторы Perl 6. Часть 1
Обзор префиксных, постфиксных и инфиксных операторов Perl 6
Многие операторы, доступные в Perl 6, понятны без объяснения даже тем, кто не знаком с Perl 5. В этой справочной статье перечислены все операторы, и там, где это необходимо, сделаны пояснения с примерами.
Операторы можно разделить на несколько групп в зависимости от их синтаксических особенностей. Это префиксы, инфиксы, постфиксы и еще несколько групп, которые будет рассмотрены в следующей части.
Префиксы (prefixes)
!
!
— логический оператор отрицания.
say !True; # False
say !(1 == 2); # True
+
+
— унарный плюс, преобразующий свой операнд к числовому контексту. Выполняемое действие аналогично вызову метода Numeric
:
my Str $price = '4' ~ '2';
my Int $amount = +$price;
say $amount; # 42
say $price.Numeric; # 42
Один из важных случаев применения унарного плюса встречался в статье про грамматики: +$/
. Эта конструкция преобразует в число объект типа Match
, который содержит данные о совпавшей части грамматики или регекса.
-
-
— унарный минус, меняющий знак у числа. Поскольку оператор неявно вызывает метод Numeric
, он может попутно преобразовать контекст, как это делает унарный плюс.
my Str $price = '4' ~ '2';
say -$price; # -42
?
?
— оператор, преобразующий контекст в логический, вызывая на объекте метод Bool
:
say ?42; # True
~
~
— оператор преобразования к строковому типу.
my Str $a = ~42;
say $a.WHAT; # (Str)
Иногда строковый контекст может быть указан неявно, например, при интерполяции переменных внутри строк в двойных кавычках.
++
++
— префиксная форма оператора инкремента. Сначала выполняется инкремент, а потом возвращается новое значение.
my $x = 41;
say ++$x; # 42
Инкремент не обязан ограничиваться только числами:
my $a = 'a';
say ++$a; # b
Особый случай — имена файлов с порядковым номером внутри (текстовые строки, содержащие число, точку и расширение):
my $f = "file001.txt";
++$f;
say $f; # file002.txt
++$f;
say $f; # file003.txt
–
--
— префиксная форма оператора декремента. Как и префиксный ++
, оператор сначала выполняет действие над операндом (декремент в данном случае), а затем возвращает результат.
my $x = 42;
say --$x; # 41
С текстовыми значениями поведение аналогично оператору ++
.
+^
+^
— побитовое отрицание с учетом двоичного дополнения.
my $x = 10;
my $y = +^$x;
say $y; # -11 (не -10)
Этот оператор может выступать и в качестве бинарного (см. дальше). Ср. также с ?^
.
?^
?^
— логическое отрицание. Важно обратить внимание, что это не инверсия битов: вначале аргумент преобразуется к булевому значению, а затем делается отрицание.
my $x = 10;
my $y = ?^$x;
say $y; # False
say $y.WHAT; # (Bool)
^
^
— оператор, создающий диапазон (объект типа Range
) от нуля до указанного значения (не включая его).
.print for ^5; # 01234
Этот пример аналогичен более явному:
.print for 0..4; # 01234
|
|
разворачивает объекты, содержащие несколько значений (например, массивы, пары или парселы), в список. Этот оператор необходимо использовать, в частности, при вызове функции, принимающей список скаляров, когда исходные данные находятся в массиве:
sub sum($a, $b) {
$a + $b
}
my @data = (10, 20);
say sum(|@data); # 30
Без оператора |
компилятор сообщит об ошибке, поскольку функция ожидает два скаляра и не готова принять массив.
not
not
преобразует операнд к логическому типу и делает отрицание. То же, что делает префиксный оператор !
.
say not False; # True
so
so
преобразует операнд к логическому типу и возвращает результат. Действие аналогично префиксу ?
.
say so 42; # True
say so True; # True
say so 0.0; # False
temp
temp
— делает переменную временной, значение которой восстановится при выходе за пределы текущей области видимости (как local
в Perl 5).
my $x = 'x';
{
temp $x = 'y';
say $x; # y
}
say $x; # x
Ср. с префиксом let
.
let
let
— префиксный оператор, позволяющий восстановить значение переменной, если выход из текущей области видимости был выполнен с исключением.
my $var = 'a';
try {
let $var = 'b';
die;
}
say $var; # a
При наличии die
пример напечатает исходное значение a
, а если строку с die
закомментировать, то выполненное в блоке присваивание сохранится, и на печати окажется b
.
Ключевое слово let
выглядит похоже на деклараторы my
, our
и другие, но является при этом префиксным оператором. Ср. с префиксом temp
.
Постфиксы (postfixes)
++
++
— постфиксный вариант инкремента. Инкрементирует операнд, но, в отличие от префиксной формы, возвращает предыдущее значение.
my $x = 42;
say $x++; # 42
say $x; # 43
–
--
— постфиксный декремент. Уменьшает значение на единицу и возвращает предыдущее значение.
И постфиксные, и префиксные операторы инкремента и декремента имеют «магическое» (magic) свойство правильно обрабатывать числа в именах файла:
my $filename = 'file01.txt';
for 1..10 {
say $filename++;
}
Этот пример печатает имена файлов file01.txt
, file02.txt
, …, file10.txt
.
Постфиксы вызова методов (method postfixes)
В Perl 6 есть несколько синтаксических конструкций, начинающихся с точки, которые несколько похожи на постфиксные операторы. Все они относятся к вызову методов на объекте.
.
.method
вызывает метод method
на переменной. Это работает как с настоящими методами классов, определенных пользователем, так и с объектами всех встроенных типов. Кроме того, при попытке вызвать метод на объекте нативного типа (такого как int
— машинное представление целого числа), будет создан объект одного из встроенных типов (в примере с int
— Int
), на котором и вызовется метод.
say "0.0".Numeric; # 0
say 42.Bool; # True
class C {
method m() {say "m()"}
}
my $c = C.new;
$c.m(); # m()
.=
.=
является мутирующим вызовом метода на объекте. Действие $x.=method
равнозначно записи $x = $x.method
.
В следующем примере контейнер $o
, первоначально содержащий объект класса C
, после вызова $o.=m()
замещается новым объектом, уже другого класса.
class D {
}
class C {
method m() {
return D.new;
}
}
my $o = C.new;
say $o.WHAT; # (C)
$o.=m();
say $o.WHAT; # (D)
.^
.^method
вызывает метод method
, но не напрямую на текущем объекте, а на метаобъекте типа HOW
. Следующие две записи эквивалентны:
my Int $i;
say $i.^methods();
say $i.HOW.methods($i);
Метаобъекты — отдельная тема, к которой есть смысл вернуться в следующий раз.
.?
.?method
вызывает метод, если он определен. Если нет, возвращает Nil
.
class C {
method m() {'m'}
}
my $c = C.new();
say $c.?m(); # m
say $c.?n(); # Nil
.+
.+method
делает попытку вызвать все методы с именем method
, доступные для объекта. Такая ситуация может возникнуть при создании иерархий объектов.
class A {
method m($x) {"A::m($x)"}
}
class B is A {
method m($x) {"B::m($x)"}
}
my $o = B.new;
my @a = $o.+m(7);
say @a; # Печатается B::m(7) A::m(7)
В этом примере объект $o
может обратиться к методу m
либо из своего класса B
, либо из родительского A
. Конструкция $o.+m(7)
выполняет вызовы обеих методов и помещает результаты в массив. Если метода с нужным именем не обнаружено, возникает исключение.
.*
.*method
вызывает все методы с именем method
, и возвращает парсел со списком результатов, либо пустой набор, если метод не определен. В остальном работает аналогично оператору .+
.
Инфиксные операторы
Инфиксные операторы стоят в программе между операндами, и могут быть бинарными (в отличие от унарных префиксных или постфиксных) или тернарными (такой оператор всего один).
Простейший пример инфиксного оператора — символ сложения +
. Справа и слева он ожидает два значения, например, две переменные: $a + $b
. Важно понимать, что один и тот же символ или одна и та же последовательность символов в разных контекстах может быть или префиксом, или инфиксом. В примере с плюсом — это определенный в Perl 6 унарный плюс, устанавливающий числовой контекст: +$str
.
Операторы для работы с числами
+, -, *, /
+
, -
, *
, /
— операторы, выполняющие соответствующие арифметические действия, они не требуют пояснения. При разговоре о Perl 6 нужно помнить, что прежде чем выполнить операцию операнды будут автоматически приведены к численному (Numeric
) типу, если это необходимо.
div
div
— целочисленное деление с округлением вниз.
say 10 div 3; # 3
say -10 div 3; # 4
%
%
— деление по модулю (остаток от целочисленного деления). Операнды при необходимости приводятся к численному типу.
mod
mod
— деление по модулю, но в отличие от %
приведение типов не выполняется. Сравните четыре примера.
Деление по модулю двух целых чисел выполняется одинаково:
say 10 % 3; # 1
say 10 mod 3; # 1
Если один из операндов сделать строкой, то оператор %
приведет ее к числу:
say 10 % "3"; # 1
А при использовании оператора mod
возникнет ошибка:
say 10 mod "3";
Calling 'infix:<mod>' will never work with argument types (Int, Str)
Expected any of:
:(Real $a, Real $b)
Поэтому преобразование необходимо выполнить явно:
say 10 mod +"3"; # 1
Или:
say 10 mod "3".Int; # 1
%%
%%
— оператор, сообщающий о возможности целочисленного деления (divisibility, «делибельность») без остатка. Возвращает булевое значение.
say 10 %% 3; # False
say 12 %% 3; # True
+&, +|, +^
+&
, +|
, +^
— побитовое умножение, сложение и XOR. Плюс в операторе намекает на то, что операнды перед выполнением действия приводятся к типу Int
.
?|, ?&, ?^
?|
, ?&
, ?^
приводят операнды к логическому типу и выполняют логические операции OR, AND и XOR.
+<, +>
+<
, +>
— операторы побитового сдвига влево или вправо.
say 8 +< 2; # 32
say 1024 +> 8; # 4
gcd
gcd
(greatest common denominator) вычисляет наибольший общий делитель двух чисел.
say 50 gcd 15; # 5
lcm
lcm
(least common multiple) находит наименьшее общее кратное.
say 1043 lcm 14; # 2086
==, !=
==
, !=
выполняют сравнение двух численных операндов. Данные приводятся к типу Numeric
при необходимости.
<, >, <=, >=
<
, >
, <=
, >=
— операторы для численного сравнения.
<=>
<=>
— оператор для сравнения чисел, возвращающий значение типа Order
: Order::Less
, Order::More
или Order::Same
.
Операторы для работы со строками
~
~
— конкатенация строк. Точка в Perl 6 теперь используется для вызова методов, а все действия, связанные со строками, используют тильду.
say "a" ~ "b"; # ab
При необходимости происходит преобразование типов:
say "N" ~ 1; # N1
say 4 ~ 2; # 42
x
x
повторяет строку указанное число раз.
say "A" x 5; # AAAAA
Нестроковые значения перед повторением будут преобразованы в строку:
say 0 x 5; # 0000
Если запрошено отрицательное число повторов, возвращается пустая строка.
eq, ne
eq
, ne
сравнивают строки (или приведенные к строке значения) на равенство и неравенство, соответственно.
lt, gt, le, ge
lt
, gt
, le
, ge
— операторы для сравнения строк: меньше, больше, меньше или равно, больше или равно. Операнды приводятся к строкам.
leg
leg
— оператор, сообщающий, равны ли строки, либо одна из них «больше» или «меньше» (в алфавитном порядке). Аналогична оператору cmp
из Perl 5 (но не из Perl 6), но возвращает значение Order::Less
, Order::More
или Order::Same
.
say "a" leg "b"; # Less
say "abc" leg "b"; # Less
say "bc" leg "b"; # More
say "abc" leg "ABC".lc; # Same
Перед сравнением все операнды приводятся к строкам.
say 42 leg "+42"; # More
say 42 leg "42"; # Same
Универсальные операторы сравнения
В Perl 6 определены несколько операторов, которые одинаково подходят для сравнения как строк, так и для чисел и даже для составных объектов (например, пар).
cmp
cmp
сравнивает два объекта и возвращает значение типа Order
— одно из Less
, Same
и More
.
say 2 cmp 2; # Same
say 2 cmp 2.0; # Same
say 1 cmp 2; # Less
say 2 cmp 1; # More
say "a" cmp "b"; # Less
say "abc" cmp "b"; # Less
say "bc" cmp "b"; # More
say "abc" cmp "ABC".lc; # Same
my %a = (a => 1);
my %b = (a => 1);
say %a cmp %b; # Same
При сравнении величин разных типов (строки с числом, например) следует быть осторожным и помнить, что в языке определены несколько мультивариантов оператора:
proto sub infix:<cmp>(Any, Any) returns Order:D is assoc<none>
multi sub infix:<cmp>(Any, Any)
multi sub infix:<cmp>(Real:D, Real:D)
multi sub infix:<cmp>(Str:D, Str:D)
multi sub infix:<cmp>(Enum:D, Enum:D)
multi sub infix:<cmp>(Version:D, Version:D)
(:D
в определении — это не смайл, а указание на то, что аргумент должен быть определен, то есть в переменной этого типа должно содержаться значение.)
Поэтому при сравнении строки с числом компилятор скорее всего выберет вариант функции с сигнатурой (Str:D, Str:D)
, и оба операнда будут рассматриваться как строки:
say "+42" cmp +42; # Less
say ~42 cmp +42; # Same
Обратите внимание на отличие этого оператора от одноименного, доступного в Perl 5. Ср. с оператором leg
.
before, after
before
, after
— универсальные операторы сравнения, работающие с числами, строками и объектами других типов. Возвращает логическое значение True
или False
в зависимости от того, какой из операндов стоит раньше или позже.
Работа с преобразованиями типов операндов аналогична оператору cmp
. Стоит иметь в виду, что в зависимости от типа данных сравнение либо чисел, либо «таких же» строк в разных случаях может привести к противоположным результатам, поскольку числа сравниваются как числа, а строки — как строки в алфавитном порядке:
say 10 before 2; # False
say '10' before '2'; # True
say 10 before 20; # True
say '10' before '20'; # True
eqv
eqv
— оператор, сравнивающий объекты на эквивалентность. Возвращает булевое значение, истинное в том случае, если оба операнда имеют одинаковый тип и содержат одинаковые данные.
my $x = 3;
my $y = 3;
say $x eqv $y; # True
Пример с более сложными данными:
my @a = (3, 4);
my @b = (3, 4, 5);
@b.pop;
say @a eqv @b; # True
Поскольку целые числа, числа с плавающей точкой и строки, содержащие только цифры, относятся к разным типам данным, следующие сравнения дадут False
:
say 42 eqv 42.0; # False
say 42 eqv "42"; # False
Важно не попасть в ловушку, когда в дело вступает тип Rat
:
say 42 eqv 84/2; # False
say 42 eqv (84/2).Int; # True
===
===
— оператор, возвращающий истинное значение в том случае, если операнды являются одним объектом, и ложное в остальных случаях.
class I {
}
# Три разных экземпляра.
my $i = I.new;
my $ii = I.new;
my $iii = I.new;
my @a = ($i, $ii, $iii);
for @a -> $a {
for @a -> $b {
say $a === $b; # Печатает True только там, где $a и $b
# указывают на один и тот же элемент
# массива @a.
}
}
=:=
=:=
— оператор для проверки того, что операнды ссылаются на один и тот же объект. Возвращается истина или ложь.
my $x = 42;
my $y := $x;
say $x =:= $y; # True
say $y =:= $x; # True
~~
~~
— оператор смартматчинга. Оператор выполняет сравнение объектов и на первый взгляд работает независимо от их типа, например:
say 42 ~~ 42.0; # True
say 42 ~~ "42"; # True
Оператор не является коммутативным, и может возвращать разные результат при перестановки операндов:
say "42.0" ~~ 42; # True
say 42 ~~ "42.0"; # False
Это поведение проясняется, если рассмотреть алгоритм работы оператора ~~
. Оператор вычисляет значение правого операнда и вызывает на нем метод ACCEPTS
, которому передает значение левого операнда (точнее, ссылку $_
на него). Для каждого типа данных существует свой вариант метода ACCEPTS
. Для строк, например, он сравнивает строки, а для чисел — числа.
Предыдущие два примера эквивалентны следующим:
say 42.ACCEPTS("42.0"); # True
say "42.0".ACCEPTS(42); # False
Операторы для работы со списками
xx
xx
повторяет список заданное число раз.
say((1, -1) xx 2); 1 -1 1 -1
Аналогично строковому x
, оператор xx
возвращает пустой список, если число повторов отрицательное или нулевое.
Z
Z
— зип-оператор (zip). Смешивает два массива, возвращая по очереди элементы то одного, то другого из них до тех пор, пока в каждом из списков достаточно элементов.
Запись
@c = @a Z @b;
эквивалентна примерно следующей:
@c = (@a[0], @b[0], @a[1], @b[1], ...);
X
X
— оператор, формирующий из двух списков третий, состоящий из всех возможных комбинаций элементов исходных массивов.
@c = @a X @b;
То же, что:
@c = (@a[0], @b[0], @a[0], @b[1], @a[0], @b[2], ..., @a[N], @b[0], @a[N], @b[1] ..., @a[N], @b[M]);
Операнды не обязательно должны быть одинаковой длины.
…
...
— оператор, создающий ленивые списки.
my @list = 1 ... 10;
В отличие от ..
, три точки умеют считать и в обратную сторону:
my @back = 10 ... 1;
Звездочка в качестве конечного элемента создаст бесконечный ленивый список:
my @long = 1 ... *;
Операторы для объединений
|, &, ^
|
, &
, ^
— операторы, создающие junctions (объединения? они же ранее известны как «квантовые суперпозиции»). Эти объекты могут использоваться на месте скаляров, но ведут себя одновременно как несколько значений.
Операторы |
, &
и ^
создают, соответственно, junctions типа any
, all
и one
. Junctions ведут себя как скаляры, содержащие одновременно несколько значений:
# Значение 4 – одно из перечисленных:
say "ok" if 4 == 1|2|3|4|5;
# 4 нет ни в каком из указанных:
say "ok" if 4 != 1 & 2 & 3 & 5;
# 4 повторяется дважды, поэтому оно не единственное:
say "ok" unless 4 == 1 ^ 2 ^ 2 ^ 4 ^ 4 ^ 5;
Shortcut-операторы
&&
&&
возвращает первый из операндов, который будучи преобразованным в логический тип становится False
. Обратите внимание, что возвращаемое значение — не True
или False
, а оригинальное значение одного из операндов (если конечно он уже не был False
).
say 10 && 0; # 0
say 0 && 10; # 0;
say 12 && 3.14 && "" && "abc"; # пустая строка
Как только найдено подходящее значение, работа оператора закончится, и все оставшиеся справа значения вычисляться не будут.
||
||
возвращает первый операнд, который в логическом контексте является True
. Последующие значения не вычисляются.
say 10 || 0; # 10
say 0 || 10; # 10
^^
^^
— возвращает операнд, который является в булевом контексте истинным, и при этом он такой единственный. Если ничего не найдено, возвращается Nil
. Как только найден второе истинное значение, вычисления оставшихся прекращаются.
say 0 ^^ '' ^^ "abc" ^^ -0.0; # abc
say 0 ^^ '' ^^ "abc" ^^ -10.0; # Nil
//
//
возвращает первый определенный (defined) операнд. Остальные при этом не вычисляются.
my $x;
my $y = 42;
my $z;
say $x // $y // $z; # 42
Другие инфиксные операторы
min, max
min
, max
возвращают, соответственно, минимальный и максимальный из своих операндов.
?? !!
?? !!
— тернарный условный оператор. Работает как ? :
в Perl 5.
say rand < 0.5 ?? 'Yes' !! 'No';
=
=
— оператор присваивания.
=>
=>
— конструктор пар. Создает пары ключ — значение, причем ключ не обязательно заключать в кавычки.
my $pair = alpha => "one";
my %data = jan => 31, feb => 28, mar => 31;
,
,
— оператор конструирования парселов (parcel).
my $what = (1, 2, 3);
say $what.WHAT; # (Parcel)
При вызове функций запятая используется как разделитель передаваемых параметров.
:
:
используется при вызове методов как разделитель аргументов. Понять работу двоеточия можно на следующем примере:
class C {
method meth($x) {
say "meth($x)";
}
}
my $o = C.new;
meth($o: 42); # Вызывается метод meth объекта $o, печатается meth(42)
После инфиксного двоеточия обязательно должен быть пробел (если его нет, двоеточие может означать именованный параметр), и оно должно стоять только после первого аргумента.
← Отладка приложений на AnyEvent | Содержание | Метаоператоры в Perl 6 →