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

Компиляция регулярных выражений, модификатор o, функция study, хронометраж

< Лекция 12 || Лекция 13: 1234 || Лекция 14 >

Предыдущее регулярное выражение было ориентировано на быстрый пропуск всего, что мы не ищем, и после пропуска всего ненужного должен появиться желанный непробельный символ вне тегов. В этот раз поступим так: будем искать непробельный символ, который не является открывающей и закрывающей угловой скобкой, а найдя его, проверим, стоит ли он внутри тега или нет. Как это запрограммировать на языке регулярных выражений? Очень просто: если символ стоит внутри тега, то с его позиции будет найден текст, соответствующий шаблону

(?=[^<>]*>)

А если этот символ стоит вне тегов, то такого текста найдено не будет, поэтому будет истинен шаблон

(?![^<>]*>)

Вот вся эта программа:

use Benchmark;

$_=' <pppp>' x 13000;
$_.='<table>a';
my $re=qr
/[^\s<>]
 (?![^<>]*>)
/x;
my $t1=new Benchmark;
for (1..1000000)
 { /$re/;
 }
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);

На печати получаем:

1 wallclock secs ( 1.09 usr +  0.00 sys =  1.09 CPU)

Мы видим, что второй вариант работает немного медленнее первого. Очевидно, это связано с тем, что ему приходится проверять каждый символ внутри тегов с помощью негативной опережающей проверки. Отсюда мы видим, что для наших данных (много тегов и мало пробелов) лучше использовать первый вариант регулярного выражения.

Теперь узнаем, в какую цену обходится встроенный код Perl. Добавим в последний пример встроенный код, который будет увеличивать счетчик:

use Benchmark;

$_=' <pppp>' x 13000;
$_.='<table>a';
my $count=0;
my $re=qr
/[^\s<>]
 (?![^<>]*>)
 (?{ ++$count })
/x;
my $t1=new Benchmark;
for (1..1000000)
 { /$re/;
 }
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
print "\n$count";

На печати появится:

3 wallclock secs ( 3.44 usr +  0.00 sys =  3.44 CPU)
1000000

Время выполнения увеличилось примерно в 3 раза. Сравним это со временем выполнения самого кода автоприращения:

use Benchmark;

my $count=0;
my $t1=new Benchmark;
for (1..1000000) { ++$count }
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);
print "\n$count";

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

0 wallclock secs ( 0.13 usr +  0.00 sys =  0.16 CPU)
1000000

Мы видим, что сам по себе этот код берет времени намного меньше, чем когда он выполняется внутри регулярного выражения.

Далее рассмотрим ресурсоемкость динамического регулярного выражения. Для этого вернемся к примеру из "лекции 12" и переделаем его для наших нужд:

use Benchmark;

$_='Далее стоит 13 нулей: 0000000000000' x 13000;
my $re=qr/(\d+)\D+(??{"0{$1}"})/;
my $t1=new Benchmark;
for (1..1000000)
 { /$re/;
 }
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);

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

4 wallclock secs ( 4.74 usr +  0.00 sys =  4.74 CPU)

А сейчас заменим динамическое регулярное выражение подшаблоном \d+:

use Benchmark;

$_='Далее стоит 13 нулей: 0000000000000' x 13000;
my $re=qr
/(\d+)\D+\d+/;
my $t1=new Benchmark;
for (1..1000000)
 { /$re/;
 }
my $t2=new Benchmark;
print timestr(timediff $t2,$t1);

В этот раз время уменьшится:

1 wallclock secs ( 1.38 usr +  0.00 sys =  1.38 CPU)

Замечаем, что встроенный код и динамические регулярные выражения даже в простом случае увеличивают время выполнения регулярного выражения примерно в 3 раза.

Аналогично заметно возрастает время выполнения оператора подстановки s/// с модификатором e. Были практические случаи, когда оператор подстановки с модификатором e замедлял работу программы в десятки раз.

< Лекция 12 || Лекция 13: 1234 || Лекция 14 >
Гулзира Урбисинова
Гулзира Урбисинова
Сергей Крупко
Сергей Крупко

Добрый день.

Я сейчас прохожу курс  повышения квалификации  - "Профессиональное веб-программирование". Мне нужно получить диплом по этому курсу. Я так полагаю нужно его оплатить чтобы получить диплом о повышении квалификации. Как мне оплатить этот курс?