Регулярные выражения Perl и их применение

       

Встроенный код и интеллектуализация поиска


Рассмотрим пример, когда встроенный код помогает сделать поиск более интеллектуальным. Путь нам надо найти в тексте самое большое натуральное число. При поиске мы используем цикл while и модификатор g. Результат будем запоминать в переменной $n, которая вначале будет иметь неопределенное значение. Во встроенном коде мы проверяем, имеет ли переменная $n определенное значение или $n меньше очередного найденного числа. Если это так, то мы присваиваем $n новое значение. В результате $n должна хранить первое попавшееся максимальное число.

my $n; $_='20 0 36 35'; while (/(\d+)(?{$n=$+ if !defined $n || $n < $+})/g) {} print "Наибольшее число - это $n" if defined $n;

Напечатается

Наибольшее число - это 36

Этот пример можно упростить, исключив из него цикл while и модификатор g. Для этого в конец регулярного выражения добавим подшаблон (?!), который ни с чем не совпадает. Это будет заставлять механизм поиска соответствия делать возвраты при переборе, а когда возвраты исчерпаются, - продвигать начальную позицию поиска на один символ и повторять поиск.

my $n; $_='20 0 36 35'; /(\d+)(?{$n=$+ if !defined $n || $n < $+})(?!)/; print "Наибольшее число - это $n" if defined $n;

Опять напечатается, что

Наибольшее число - это 36

Относительно этого красивого примера хочу сделать такое замечание: если вы распечатаете начальные позиции поиска, то обнаружите, что благодаря работе условной конструкции (?!) поиск стартует, начиная с каждой цифры, т.е. проверяются также "числа" 6 и 5. И только по счастливой случайности это не привело к ошибке в результате. Вообще говоря, перед подшаблоном (\d+) надо поставить условие, что слева нет цифры, тогда поиск будет начинаться только с начала чисел:

my $n; $_='20 0 36 35'; /(?<!\d)(\d+)(?{$n=$+ if !defined($n) || $n < $+})(?!)/; print "Наибольшее число - это $n" if defined $n;

Обратите внимание, что поиск при использовании подшаблона (?!), если он не стоит в альтернативной конструкции и условном операторе, всегда заканчивается неудачей, а такой оператор поиска используется только ради побочных эффектов (установки нумерованных переменных), поэтому неправильно вставлять подобный оператор в заголовок цикла while и в условие оператора if.

А сейчас распространим этот пример на отрицательные числа. Надо учитывать знак минус перед числом. С циклом while и модификатором g все работает так же, как и раньше:

my ($n,$tmp); $_='-200 0 36 35'; while( /(-)? # берем минус в $1, если он есть (\d+) # берем число в $2 (?{ $tmp=$1 ? -$2 : $2; # в $tmp получаем число с учетом знака $n=$tmp if !defined $n || $n < $tmp; }) /gx) {}; print "Наибольшее число - это $n" if defined $n;


Для удобства я применил модификатор x и комментарии. Если переменная $1 определена, то мы в тернарном условном операторе учитываем, что число отрицательное; если $1 имеет неопределенное значение, то берем число из $2 таким, как есть. В операторе

$n=$tmp if !defined($n) || $n < $tmp;

мы не можем аналогично написать

$n=$tmp if !$n || $n < $tmp;

потому что значения $n==0 и $n==undefined будут неразличимы.

В итоге на печать выходит строка

Наибольшее число - это 36

В более красивом случае нужно позаботиться о том, чтобы поиск не начинался сразу после знака минус и чтобы перед подшаблоном (\d+) не было цифры. И все число неплохо заключить в атомарные скобки, т.к. число должно состоять из всех встретившихся подряд цифр и знака минус, если он был.

my ($n,$tmp); $_='-200 0 36 35'; /(?<!-) # перед стартовой позицией не должно быть минуса (?> # атомарная группировка для всего числа (-)? # берем минус в $1, если он есть (?<![\d]) # перед числом не должно быть цифры (\d+) # берем число в $2 ) (?{ $tmp=$1 ? -$2 : $2; # в $tmp получаем число с учетом знака $n=$tmp if !defined $n || $n < $tmp; }) (?!) /x; print "Наибольшее число это $n" if defined $n;

И опять на печать выходит, что

Наибольшее число - это 36


Содержание раздела