Опубликован: 18.06.2007 | Доступ: свободный | Студентов: 1354 / 35 | Оценка: 4.14 / 3.29 | Длительность: 12:44:00
ISBN: 978-5-94774-604-4
Лекция 9:

Встроенный код, переменные local и my

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

Рассмотрим пример, когда встроенный код помогает сделать поиск более интеллектуальным. Пусть нам надо найти в тексте самое большое натуральное число. При поиске мы используем цикл 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
Константин Бражников
Константин Бражников
Россия
Mike .
Mike .
Россия