Некоторые сведения о Perl 5/Синтаксис

Синтаксис Perl 5, хотя и наследует некоторые черты таких языков, как Awk и C (и, следовательно, ведет свою родословную от синтаксиса Алгол 68 — как и, по-видимому, большинство распространенных ныне языков программирования), является, пожалуй, одной из наиболее сложных черт языка. В частности — благодаря наличию зачастую множества форм записи одного и того же по своему смыслу кода, а также неочевидности (или, во всяком случае, — непривычности) некоторых своих особенностей.

Примеры неочевидных по смыслу форм

править

Рассмотрим, для примера, следующий фрагмент кода, выбирающий из списка (Shanghai, Karachi, Mumbai, Delhi, Ahmedabad) строки, содержащие латинскую букву «b»:

grep { /b/ } ("Shanghai", "Karachi",  "Mumbai",
              "Delhi", "Ahmedabad");

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

print (grep { /b/ }  ("Shanghai", "Karachi",  "Mumbai",
                      "Delhi", "Ahmedabad"),
       "\n");

Однако, на деле перевод строки не будет выведен, поскольку с точки зрения Perl 5, строка "\n" является… аргументом функции grep, — отнюдь не print.

Исправить ситуацию в данном случае может дополнительная пара скобок вокруг вызова grep:

print ((grep { /a/ } ("Shanghai", "Karachi",  "Mumbai",
                      "Delhi", "Ahmedabad")),
       "\n");

Контексты

править

Выражения и утверждения

править

Как и некоторые другие языки, Perl 5 предполагает два основных синтаксических контекста:

  • выражений (expressions);
  • утверждений (statements.)

В простейшем случае, утверждение состоит из одного выражения — присваивания, вызова функции, или иного. Например:

  • my $a = 3; — объявление скаляра $a в текущей области видимости с начальным значением 3;
  • print ($a, "\n"); — вызов функции print с передачей в качестве аргументов скаляра $a (другими словами, — ссылки на данный скаляр) и строки "\n".

Переход из контекста утверждений в контекст выражений выполняется «автоматически.» Обратный переход возможен благодаря форме do { }, например:

my $a = 42;
my $b = 1 + do {
    my $b = f ($a);
    ## .
    g ($b) * h ($b);
};

Как видно из примеров выше, в качестве разделителя утверждений используется точка с запятой ;. Использование разделителя после последнего оператора в { }-блоке не обязательно. (Однако едва ли найдется веская причина опускать оный в многооператорных, многострочных блоках.)

Скалярный и векторный контексты

править

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

  • ожидания скаляра (скалярный контекст);
  • ожидания вектора (массива, списка.)

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

my $a
    = grep { /a/ } ("Shanghai", "Karachi",  "Mumbai",
                    "Delhi", "Ahmedabad");
print ($a, "\n");
## → 4

Как правило, при вызове в скалярном контексте функции, возвращающей вектор, значением является первый элемент возвращаемого вектора. Однако, функция grep определена так, что в скалярном контексте возвращает количество элементов переданного ей списка, удовлетворяющих заданному условию. (Следовательно, в примере выше, значением $a окажется 4.)

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

my @counts = (scalar (@foo), scalar (@bar));

(В то время как без scalar, запись (@foo, @bar) означает объединение списков.)

Функция wantarray возвращает «истину» при вычислении в контексте ожидания вектора и «ложь» в противном случае, что можно использовать при создании функций, имеющих различный смысл в этих контекстах.

Константы

править

Perl 5 поддерживает числовые константы (как целочисленные, так и с плавающей точкой) «обычного вида» (1, -2, 1.23e4), а также ряд форм для записи строковых и списковых констант, а также строковых «полуконстант-шаблонов» в стиле Bourne shell. В частности:

  • 'hello', q (hello) — строковые константы;
  • "hello, $a", qq (hello, $a) — строки, допускающие подстановку переменных («шаблоны») и особых символов, а также экранирование;
  • qw (hello world) — список из двух элементов (hello и world);
  • <<разделитель — строковая константа (если разделитель взят в одинарные кавычки или форму q ( )) или «строка-шаблон» (в противном случае), состоящая из строк начиная со следующей и до первой следующей за ней строки программы, совпадающей с разделителем.

В формах " " и qw ( ) доступны следующие \-подстановки:

Примеры (где — перевод строки):

"print \"Hello!\\n";";  # → print "Hello!\n";
qq (-\) \\/ \(-\n);     # → -) \/ (-␤
"\N{U+263a}\n";         # → ☺␤

Отметим, что формы q ( ), qq ( ), qw ( ) (и им подобные) допускают произвольный парный (скобки) или непарный символ в качестве разделителя, подобно: q ~hi~, qw [hello world].

Использование формы q { } особенно удобно в тех случаях, когда строковая константа содержит достаточно широкий набор символов, однако все скобки указанного вида в ней корректно спарены. В частности, когда такая константа является фрагментом кода на каком-либо из «Алгол-подобных» языков программирования. Например:

print (q [print $a[$b[$c]];], "\n");

Переменные

править

Переменные в Perl обозначаются символами $ (скаляры), @ (векторы или массивы) и % (таблицы или ассоциативные массивы) перед именем. Например (где для формирования списков для инициализации переменных @name и %favorites используются во многом равнозначные списковые операторы , и =>):

my $answer = 42;
my @name = ("Smith", "John");
my %favorites
    =  ("color" => "green",
        "fruit" => "watermelon");

Для объявления переменных используются следующие формы:

  • my — объявление переменной или переменных с областью видимости — «наиболее вложенным» из текущих блоков;
  • our — область видимости — текущий модуль;
  • state — аналогично my, но переменные не будут переинициализированы при повторных выполнениях блока.

Объявить переменные можно в произвольной точке блока. Также, объявление может быть объединено с присваиванием переменным начальных значений (как в примерах выше.) В случае отсутствия такого присваивания, начальным значением объявленных переменных окажется особое «неопределенное» значение (undef.) Для проверки на «определенность» используется функция defined, подобно:

my $a;
print "\$a is undefined\n"
    unless (defined ($a));
## → $a is undefined␤

Кроме того, доступна форма local, позволяющая присвоить значение переменной до завершения текущего блока, полезная в первую очередь для изменения особых переменных Perl,[1] таких как разделитель полей $OFS и разделитель записей $ORS (в контексте действия директивы use English;). Например:

use English qw (-no_match_vars);
local ($OFS, $ORS)
    = (" ", "\n");
print ("Answer:", $answer);
## → Answer: 42␤

Отметим, что объявление переменных является обязательным в контексте действия директив use strict; и use common::sense;, которые автор считает должным рекомендовать для повсеместного использования.

Обращение к элементам списков и таблиц выполняется формами [индекс] и {ключ}, соответственно. При этом:

  • индекс первого элемента вектора — 0 (иными словами: смещение, не порядковый номер);
  • перед именем переменной указывается символ целевого типа ($ или @);
  • в качестве индексов и ключей могут, в свою очередь, также использоваться списки.

Например (продолжая примеры выше):

print ("Name:", @name[(1, 0)]);
## → Name: John Smith␤
print ("Favorite color:", $favorites{"color"});
## → Favorite color: green␤

Примерами сложных утверждений являются циклы, частным случаем которых (!) является безусловный цикл-блок,[2] в том числе и используемый после модификаторов if и unless. Например:

foreach my $i  ("Shanghai", "Karachi",  "Mumbai") {
    if ($i =~ /b/) { next; }
    print ("  ", $i, "\n");
}
{
    my $a = "Hello, world!";
    print ($a, "\n");
}

Может показаться странным, что блок ({ }) назван частным случаем цикла, однако вне зависимости от наличия образующего цикл модификатора (for, foreach, until, while) в таких блоках доступны обычные операторы управления циклами — last, next и redo. («Истинным» блоком можно считать блок, образуемый формой do { } — которая, однако, сама по себе является выражением, и может требовать завершающую утверждение ;.)

Примечательно, что в то время как операторы управления циклом, примененные в «безусловном» блоке, управляют выполнением самого такого блока, оказавшись под условием они начинают управлять внешним по отношению к этому блоку циклом. Так, выполнение следующего цикла останавливается на элементе "Mumbai", а вовсе не на первом же элементе (как может показаться исходя из наличия безусловного { last; }):

foreach my $i  ("Shanghai", "Karachi",  "Mumbai") {
    { last; }
    if ($i =~ /b/) { last; }
    print ("  ", $i, "\n");
}

Модификаторы

править

Роль, которую в других языках нередко играют операторы цикла и условный оператор (контекста утверждений), в Perl 5 отдана модификаторам утверждений, основными из которых являются:

  • if выражение — выполнить модифицируемый оператор, если значение выражения — истинно;
  • unless выражение — если значение выражения — ложно;
  • while выражение — выполнять, пока значение выражения — истинно;
  • until выражение — выполнять, пока значение выражения — ложно (другими словами: до тех пор, пока не станет истинным);
  • foreach список — выполнить для каждого элемента списка.

Модификатор указывается или непосредственно после модифицируемого простого утверждения, или непосредственно перед цикл-блоком, например:

print "0xCAFE\n"
    if 0x1337;
foreach (1 .. 3) {
    print ($_, "\n");
}

Операторы

править

Perl 5 поддерживает «C-подобный» набор унарных и бинарных операторов (включая и операторы с присваиванием), и единственный тернарный условный оператор ? :.[3] В отличие от C, однако, конкретные классы могут переопределять эти операторы. Далее мы рассмотрим действия операторов «по-умолчанию.»

Арифметико-логические операторы
+, -, *, /, %, **
сложение, вычитание, умножение, частное и остаток от деления, возведение в степень, соответственно;
операторы + и - также могут пониматься как унарные (сохранение и обращение знака, соответственно);
<, <=, ==, >=, >, !=
операторы сравнения (меньше, меньше или равно, равно, больше или равно, больше, не равно);
<=>
знак разности; результатом $a <=> $b будет -1 если $a меньше $b, 0 в случае равенства, +1 если больше, и «не определено» (undef) если любой из аргументов — «не число» (NaN);
!, not
логическое отрицание; результат ! $a — истина, если $a — ложно, и наоборот; оператор not отличается более низким приоритетом;
xor
логическое «исключающее или»; результат $a xor $b — ложь, если $a и $b оба истинны или оба ложны; истина — в противном случае;
&, |, ^
поразрядные битовые умножение, сложение и сложение по модулю 2; пример: 123 & 456 → 72 (001111011₂ ⊙ 111001000₂ = 001001000₂);
эти же операции действительны для строк, понимая их как битовые векторы; так, "42" & "13"02 (0011010000110010₂ ⊙ 0011000100110011₂ = 0011000000110010₂); отметим, однако, что в понимании конкретного значения как числа или строки, Perl может следовать весьма неочевидной логике;
~
поразрядное битовое дополнение (отрицание); так, (~ 42) & 077 → 21 (11⋯11010101₂ ⊙ 111111₂ = 10101₂);
подобно &, | и ^ выше, эта операция действительна и для строк;
<<, >>
целочисленный битовый сдвиг (умножение и деление на 2ⁿ.)
Условные операторы
? :
тернарный условный оператор контекста выражения; в форме c () ? t () : f () первым вычисляется выражение c (); если результат — истинное значение, — вычисляется t (); в противном случае — вычисляется f (); последнее вычисленное выражение — результат оператора в целом;
&&, ||, //
условные операторы «и», «или», «первое определенное»:
  • в форме a () && b () первым вычисляется выражение a () и, если оно истинно, — вычисляется b ();
  • в форме a () || b ()b () вычисляется в случае, если a () — ложно;
  • в форме a () // b ()b () вычисляется в случае, если a () — «не определено» (undef);
последнее вычисленное выражение — результат оператора в целом;
and, or
аналогичны &&, ||, однако имеют приоритет меньший, чем присваивание; полезны в форме my $r = frob ($arg) or die ();.
Строковые операторы
.
объединение строк;
x
повтор строки; так, результат "a" x 5"aaaaa";
lt, le, eq, ge, gt, ne, cmp
сравнение строк; подобно <, <=, ==, >=, >, !=, и <=>, соответственно.
Списковые операторы
,
объединение списков (включая списки, состоящие из одного элемента); так, (qw (a b), ("c", "d"), "e") — список, состоящий из элементов a, b, c, d, e;
=>
аналогично ,, однако понимает аргумент слева как строку, если оный является идентификатором (состоит только из букв, цифр и знака _, и не начинается с цифры); так, (a => "b") — список a, b; в отличие от (a, "b"), где a — вызов функции a;
x
повтор списка; так, результат ("a", "b") x 2 — список a, b, a, b.
Изменяющие операторы
=
простое присваивание.
++, --
инкремент и декремент; значением $a++ является значение $a до изменения, ++$aпосле; аналогично и для --;
+=, -=, *=, /=, %=, **=
арифметические операции (см. выше) с присваиванием результата; так, $a[f ($b)] += $c равносильно $a[f ($b)] = $a[f ($b)] + $c за тем лишь исключением, что f ($b) в первом случае будет вычислено единожды;
&=, |=, ^=
поразрядные битовые операции с присваиванием результата;
<<=, >>=
целочисленный битовый сдвиг с присваиванием результата;
&&=, ||=, //=
условные операторы с присваиванием результата; $a &&= $b выполнит присваивание $a = $b, если $a — истинно; $a ||= $b — если $a — ложно; $a //= $b — если $a — «не определено» (undef);
.=
объединение строк с присваиванием результата;
x=
повтор строки с присваиванием результата.
Операторы ссылок (references)
\
ссылка на л-значение (другими словами, на выражение, которое может стоять слева от оператора присваивания =); так, \@a — ссылка на «ячейку памяти», хранящую вектор @a;
->
обращение по ссылке; применяется для:
  • обращения к элементу вектора или таблицы по ссылке; так, если $href — ссылка на таблицу, то $href->{"color"} — элемент этой таблицы с ключом color;
  • вызова функции по ссылке; подобно: $fn_ref->(@args);
  • вызова метода класса или объекта, подобно: Local::Class->new (@args) (метод new класса Local::Class) и $obj->frob (@args) (метод frob объекта, ссылка на который хранится в $obj);
$, @, %, &
не операторы; будучи поставлены перед выражением, данные символы позволяют обратиться к скаляру, вектору, таблице и функции, на который ссылается данное выражение; «сложные» выражения следует заключать в фигурные скобки, подобно: ${frob_ref (@args) // $default_ref} — обращение к скаляру, на который ссылается скаляр, возвращаемый функцией frob_ref, или на который ссылается $default_ref, если возвращенное frob_ref значение — не определено.
Скалярный оператор ,
в скалярном контексте, форма a (), b () вычисляет выражения a (), b (), и возвращает результат последнего; применяется в случаях, когда первое из выражений вычисляется только ради побочного действия, подобно: ($a++, $b++).

Ссылки

править
  1. https://metacpan.org/pod/perlvar#SPECIAL-VARIABLES
  2. https://metacpan.org/pod/perlsyn#Compound-Statements
  3. https://metacpan.org/pod/perlop