В недавно вышедшем релизе Perl 5.10.1 слегка изменилась работа оператора when.
В частности, оператор when теперь понимает конструкцию «флип-флоп».
«Флип-флоп» — это оператор диапазона .. в булевом контексте. В документации приводится пример выражения для поиска POD-комментариев:
when (/^=begin/ .. /^=end/) {
# do something
}
Аналогично возможно, например, находить определение функций во многих языках программирования, в частности, PIR:
use v5.10.1;
my $lineno = 0;
for (<DATA>) {
chomp;
print 'Line ', ++$lineno, ': ';
when (/^\.HLL/) {say "switching language to $_"}
when (/^\.sub/ .. /^\.end/) {say "subroutine body: $_"}
}
__DATA__
.HLL unknown
.sub main :main
say "Demonstrating inc"
$I = 2
inc $I
say $I
.end
Эта программа напечатает комментарии к каждой строке кода, объясняя его назначение:
Line 1: switching language to .HLL unknown
Line 2: subroutine body: .sub main :main
Line 3: subroutine body: say "Demonstrating inc"
Line 4: subroutine body: $I = 2
Line 5: subroutine body: inc $I
Line 6: subroutine body: say $I
Line 7: subroutine body: .end
Cледует обратить внимание на два момента. Во-первых, выражение, переданное when, срабатывает на всех строках подпрограммы, включая первую и последнюю, заданные границами /\^.sub/
и /\^.end/
.
Во-вторых, оператор ведет себя «нежадно», что позволяет находить непересекающиеся последовательности. Вот программа, в которую дописана дополнительная подпрограмма на PIR, и строка when, находящая пустую строку:
use v5.10.1;
my $lineno = 0;
for (<DATA>) {
chomp;
print 'Line ', ++$lineno, ': ';
when (/^\.HLL/) {say "switching language to $_"}
when (/^\.sub/ .. /^\.end/) {say "subroutine body: $_"}
when (/^$/) {say "empty line"}
}
__DATA__
.HLL unknown
.sub main :main
say "Demonstrating inc"
$I = 2
inc $I
say $I
.end
.sub another
say 'Hey, you!'
.end
Вывод программы подтверждает правильность ее работы:
Line 1: switching language to .HLL unknown
Line 2: subroutine body: .sub main :main
Line 3: subroutine body: say "Demonstrating inc"
Line 4: subroutine body: $I = 2
Line 5: subroutine body: inc $I
Line 6: subroutine body: say $I
Line 7: subroutine body: .end
Line 8: empty line
Line 9: subroutine body: .sub another
Line 10: subroutine body: say 'Hey, you!'
Line 11: subroutine body: .end
Другой пример использования — поиск и сохранение фрагмента алфавитно отсортированного списке, как это делают в бумажных словарях по двум-трем начальным буквам.
use v5.10.1;
my @programming_languages = qw(
Pascal Pawn PCASTL PCF PEARL Perl PHP Phrogram Pico Pict Piet Pike PIKT PILOT Pizza PL/0 PL/B PL/C PL/I PL/M PL/P PL/SQL PL360 PLANC Plankalkül PLEX PLEXIL Pliant POP-11 Poplog PostScript PortablE Powerhouse PPL Processing Prograph PROIV Prolog Promela PROTEL Proteus ProvideX Pure Python
);
for (@programming_languages) {
when (/^pa/i .. /^pi/i) {push @page, $_}
}
pop @page;
say join ', ', @page;
Программа подготовит данные для страницы словаря PA—PI:
Pascal, Pawn, PCASTL, PCF, PEARL, Perl, PHP, Phrogram
Разумеется, в последнем примере возможно было воспользоваться одним регулярным выражением:
my @page = ();
for (@programming_languages) {
when (/^p[a-h]/i) {push @page, $_}
}
say join ', ', @page;
Но первый вариант в данном случае более нагляден и, к тому же, более расширяем. Напрмер, метод возможно вынести в отдельную функцию и передавать ей границы диапазона в виде параметров:
say join ', ', select_range('pa', 'pi');
sub select_range {
my ($from, $to) = @_;
my @page = ();
for (@programming_languages) {
when (/^$from/i .. /^$to/i) {push @page, $_}
}
pop @page;
return @page;
}