Выпуск 16. Июнь 2014
← От редактора | Содержание | Что нового в Perl 5.20.0 →Синтаксические новинки в Perl 5.20
Рассказ о том, что нового появилось в синтаксисе.
27 мая вышел очередной стабильный релиз за номером 5.20. Подробное описание всех сделанных изменений доступно в файле perldelta, его перевод опубликован в этом номере журнала. В этой же статье я более подробно расскажу про изменения, которые были внесены в синтаксис языка.
Сигнатуры
(О сигнатурах журнал уже писал в одном из предыдущих выпусков.)
Формальные аргументы функций (подпрограмм sub) теперь возможно перечислять, причем вместе с именами, непосредственно в заголовке функции. Например:
sub fahrenheit2celsius($temperature) {
return 5 / 9 * ($temperature - 32);
}
Аргумент, переданный первым параметром, внутри тела функции становится доступным под именем $temperature
. Дополнительно объявлять эту переменную не требуется (обратите внимание на отсутствие ключевого слова my
).
Использование функции ничем не отличается от обычного способа:
say fahrenheit2celsius(100);
Важно помнить, что эта возможность считается экспериментальной, поэтому следует быть осторожным при ее использовании в ответственных приложениях. Экспериментальный статус вынуждает явно сообщать компилятору о своем намерении. Полный код работающей программы, которая компилируется и не выдает предупреждений, выглядит так:
use v5.20;
use feature 'signatures';
no warnings 'experimental::signatures';
sub fahrenheit2celsius($temperature) {
return 5 / 9 * ($temperature - 32);
}
say fahrenheit2celsius(100);
Директива use feature 'signatures'
включает сигнатуры (несмотря на наличие use v5.20
, автоматически этого не происходит), а no warnings 'experimental::signatures'
подавляет сообщение The signatures feature is experimental at signatures.pl line 6
.
При использовании сигнатур аргументы передаются по значению, поэтому внутри функции допустимо их изменять. Например, показанный пример возможно переписать следующим образом:
sub fahrenheit2celsius($temperature) {
$temperature -= 32;
return 5 / 9 * $temperature;
}
my $F = 100;
my $C = fahrenheit2celsius($F);
say "$F F = $C C";
Использование сигнатур никак не отражается на обоработке массива @_
. Поэтому вполне допустимо (хотя и непонятно, зачем) использовать его для доступа к переданным аргументам:
sub fahrenheit2celsius($temperature) {
my ($t) = @_;
return 5 / 9 * ($t - 32);
}
Использование оператора shift
для чтения аргументов из массива @_
тоже никак не затрагивает переменную $temperature
, указанную в заголовке. Переданный аргумент исчезнет из @_
, но останется в $temperature
.
Однако, коль скоро функция объявлена с перечнем аргументов, все попытки ее использовать в коде будут проверяться на наличие аргументов и их правильного числа. Несовпадение числа аргументов породит ошибку на этапе выполнения программы Too many arguments for subroutine
или Too few arguments for subroutine
.
Примечение. Ошибка возникает именно на этапе выполнения, а не при компиляции. В этом легко убедиться, напечатав что-нибудь до ошибочного вызова функции:
sub f($arg) {
say "Never reached";
}
say "Start";
f(10, 20);
Аналогично, допустимо объявить и требовать при вызове пустой список параметров:
sub justcallme() {
say "Called";
}
В этом случае безошибочным будет только вызов без аргументов: justcallme();
или без скобок: justcallme;
.
В списке параметров разрешено указывать значения по умолчанию (очевидно, что все они должны быть последними в списке, иначе компилятор в некоторых случаях не сможет разобраться, где переданы какие параметры). Синтаксис значений вполне очевидный:
sub print_envelope_label(
$name, $last_name,
$street, $house_number,
$city = 'Moscow', $country = 'Russia') {
say "$name $last_name\n$street, $house_number\n$city, $country";
}
Если в этом примере не указать город и страну, будет напечатана этикетка для конверта с адресом в Дефолт-сити:
print_envelope_label(
'Konstantin', 'Konstantinopolsky',
'Konstantinovskaya', 10,
'Kiev', 'Ukraine'
);
print_envelope_label(
'Ivan', 'Ivanov',
'Tverskaya', 4
);
В качестве значений по умолчанию возможно использовать любые выражения (они будут вычисляться каждый раз в момент вызова функции), в том числе значения уже переданных аргументов.
В том случае, если необходимо передать произвольное число аргументов, допустимо воспользоваться переменной-списком или хешем:
sub numerate_list(@list) {
my $c = 1;
for my $value (@list) {
say "$c. $value";
$c++;
}
}
numerate_list('alpha', 'beta', 'gamma');
Вариант с хешом:
sub dump_hash(%hash) {
for my $key (sort keys %hash) {
say "$key = $hash{$key}";
}
}
dump_hash(a => 'alpha', b => 'beta', c => 'gamma');
Опять же, такие аргументы должны следовать в конце списка аргументов. Идеологически эти варианты похожи на традиционное чтение аргументов из массива @_
.
Наконец, если в списке формальных аргументов окажутся переменные без имен (то есть голые сигилы $
, @
или %
), то соответствующие аргументы окажутся обязательными, но внутри функции их значения по именам доступны не будут. Такая возможность окажется полезной, например, при написании тестов или эмулировании некоего интерфейса:
sub mock_this($key, $, $value) {
say "$key = $value";
}
mock_this(1, 2, 3);
Несмотря на то, что второй аргумент по имени недоступен, его по-прежнему возможно прочитать стандартным способом: say $_[1]
.
Прототипы
Еще одно изменение, связанное с объявлением функций, — новое ключевое слово prototype
. Синтаксис описания прототипа похож на синтаксис атрибутов функций:
use v5.20;
use feature 'signatures';
sub sum :prototype($$) {
my ($x, $y) = @_;
return $x + $y;
}
say sum(10, 20);
Похоже, что отдельное ключевое слово появилось из-за того, что включение в язык сигнатур сделало неоднозначным объявления типа sub f($)
. С одной стороны, это функция, которая принимает один неиспользуемый скаляр (с точки зрения сигнатуры), а с другой — обязятельный скаляр (с точки зрения традиционных прототипов). В этом примере это одно и то же, но тем не менее, при наличии директивы use feature 'signatures';
традиционный синтаксис описания прототипа (когда в скобках за именем функции без запятых или после точки с запятой перечисляются сигилы формальных параметров) перестает работать и компилятор сообщает об ошибке:
Parse error at prototype_error.pl line 6.
syntax error at prototype_error.pl line 6, near "$$) "
Прототипы в новом виде совместимы с сигнатурами. Показанный пример с функцией sum
в этом случае будет выглядеть таким образом, что позволяет сэкономить одну строку кода:
sub sum :prototype($$) ($x, $y) {
return $x + $y;
}
Именованные аргументы должны быть указаны только после объявления прототипа, непосредственно перед блоком с телом функции.
Казалось бы, указывать прототипы одновременно с сигнатурами избыточно. Однако, в некоторых случаях это может пригодиться. Например, функция с сигнатурой умеет разворачивать переданные ей списки. Поэтому, следующий пример полностью работоспособен, хотя функции передан массив вместо двух скаляров:
sub sum($x, $y) {
return $x + $y;
}
my @values = (10, 20);
say sum(@values);
Уточнение sub sum :prototype($$) ($x, $y)
запрещает такое поведение. Если настаивать на передаче списка, то Perl сообщит об ошибке:
Not enough arguments for main::sum at prototype4.pl line 15, near "@values)"
Execution of prototype4.pl aborted due to compilation errors.
Обратите внимание, что ошибка произошла во время компиляции (однако, безошибочный код в блоке BEGIN по-прежнему может быть выполнен).
Читателю предоставляется возможность самостоятельно разобраться в том, как изменились правила в описании прототипа, связанные с наличием пробелов, точек за запятой и звездочками.
Срезы с индексами
Массивы и хеши получили возможность рассказать о своей структуре, вернув список пар ключ — значение (для хешей) либо пар индекс — значение (для массивов). Документация описывает этот момент крайне скупо, а для того, чтобы заметить новинку, потребуется внимательность.
Новое заключается в том, что используется обычный синтаксис среза, но при этом на месте сигила стоит символ %
. И для хешей, и для массивов. После сигила следует имя соответствующей переменной, а затем в фигурных или квадратных скобках перечисляются ключи или индексы. Например:
my %suffix = (pl => 'Perl', pm => 'Perl module', p6 => 'Perl 6');
my @data = %suffix{'pl', 'p6'};
say Dumper(\@data);
my @greek = qw(alpha beta gamma delta epsilon);
@data = %greek[1, 2];
say Dumper(\@data);
Выражение %suffix{'pl', 'p6'}
вернет список ключей и значений хеша %suffix
:
$VAR1 = [
'pl',
'Perl',
'p6',
'Perl 6'
];
А выражение %greek[1, 2]
— индексы и соответствующие значения:
$VAR1 = [
1,
'beta',
2,
'gamma'
];
Если намеренно или ошибочно поставить сигил списка: @suffix{'pl', 'p6'}
и @greek[1, 2]
, то программа останется синтаксически верной, но работать будет совершенно иначе:
$VAR1 = [
'Perl',
'Perl 6'
];
$VAR1 = [
'beta',
'gamma'
];
(Впрочем, ошибочный сигил $
в обоих случаях тоже будет успешно скомпилирован, даже включенный use warnings
найдет не все.)
← От редактора | Содержание | Что нового в Perl 5.20.0 →