| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Perl: Регулярные выражения > Объясните вывод re |
| Автор: MasterOfRegex 16.2.2011, 12:28 |
| print "Match: \$1=$1" if 'abcde' =~ /^((\w\w+)(?{print defined $2 ? "-$2-\n" : "--\n"})){2}$/; Выводит: -abcde- -- -- -de- Match: $1=de Объясните, почему 2 раза $2 оказался undefined, ведь после выхода за 2-ю захватывающую скобку в $2 должно быть минимум 2 символа. Сергей Статьи по http://www.cronc.com/ru.shtml |
| Автор: alezzz 16.2.2011, 21:25 |
| Не претендую на гениальность, но мысль такая: Нужно получить из выражения (\w\w+) и еще раз (\w\w+). Первый проход для первого (\w\w+), захватили "a", далее для \w+ захватили "b" и все до конка строки, т.е. "bcde", все выражение попадает в $2. Далее нужно найти второй (\w\w+). Начинаются возвраты. Первый возврат: получили \w, второй возврат: получили \w+ в количистве 1-ого символа, т.к. достаточно. Так вот, там где $2 undef -- это, видимо, и есть 2 возврата. print "Match: \$1=$1" if 'abcdef' =~ /^((\w\w{2,})(?{print defined $2 ? "-$2-\n" : "--\n"})){2}$/; тут нужно 3 возврата, выводит: -abcdef- -- -- -- -def- Match: $1=def |
| Автор: MasterOfRegex 17.2.2011, 10:54 |
| Здесь наличие возвратов не должно играть роли, т.к. 2-я пара скобок, по логике, всегда участвует в совпадении и что-то захватывает. Классики учили, что в этом случае после закрытия скобки формируются переменные $2, $+ и $^N, а также обратная ссылка \2. Здесь всё это имеет неопред. значение, как будто 2-я пара скобок не участвовала в совпадении. \w можно убрать, оставив только \w+, e из строки тоже можно убрать: #!perl -w use strict; use re qw(debug); print "Match: \$1=$1 \$2=$2" if 'abcd' =~ /^((\w+) (?{print defined $2 ? "\$2=$2\n" : "\$2 not defined\n"}) ){2}$ /x; Если вставить use re qw(debug); то выведется это re на псевдокоде и его работа, из которой видно, что вторая пара скобок закрывается, когда текущая позиция в строке находится в её конце... Как же в этом случае можно идти дальше за скобку, непонятно... Это похоже на какую-то ошибку... Попробую написать на ...@perl.org. Сергей === cd тоже можно убрать: #!perl -w use strict; use re qw(debug); print "Match: \$1=$1 \$2=$2" if 'ab' =~ /^((\w+) (?{print defined $2 ? "\$2=$2\n" : "\$2 not defined\n"}) ){2}$ /x; Выводит: $2=ab $2 not defined $2=b Match: $1=b $2=b Хм, вроде я понял, ошибок тут, похоже, нет, зря гуру с ...@perl.org беспокоил... Undefined выводится, когда предыдущий захват 2-й парой скобок откачен, а нового захвата ещё не было. Хотя в re мы находимся за 2-й парой скобок. Вот так ставить квантификаторы к захватывающим скобкам... ;-\ |
| Автор: MasterOfRegex 17.2.2011, 19:55 |
| Да и ^ тоже можно убрать. Я над этим серьёзно ещё не думал, а сейчас подумал раза 2 и считаю, что это всё-таки ошибка. Поспешил я со 2-м письмом в perl.org, ладно, посмотрим, что завтра-послезавтра оттуда ответят. Вот мой разбор работы этого re, как я понимаю этот механизм. Итак, имеем: 'ab' =~ /((\w+)(?{print defined $2 ? "\$2=$2\n" : "\$2 not defined\n"})){2}/; Оно выводит: $2=ab $2 not defined $2=b Проблема: почему $2 not defined? Ведь мы визуально только что перед этим выводом закрыли 2-ю скобку и должны получить $2 (а также $+, $^N и \2 определёнными, т.к. 2-я пара скобок только что участвовала в совпадении. А они все неопределённые. Что-то не то... Я подозреваю, что возможная ошибка возникла от того, что нумерованные переменные (и вообще спец. переменные re) локализуются не только при входе в каждый блок программы (т.е. имеют структуру стека)), но и внутри re, а это неправильно. При об-ке этого re оно должно как бы раскручиваться: аналогично тому, как \w{2} эквивалентно \w\w, так и всё re эквивалентно ((\w+)({print ...})((\w+)({print ...}) Но при этом во 2-м экз. скобки нумеруются тоже 1 и 2 и порождают $1 и $2. Вначале 1-е \w+ захватит всё ab и на печать выйдет $2=ab. Затем входим во 2-й экз. re и когда входим во 2-1 экз. 2-й скобки, $2 получает значение undefined. Видим, что \w не совпадает. Не беда, у нас есть сохр. состояние, возвращаемся к 1-му экз. 2-й скобки. Но при возврате мы входим справа в 1-й экз. 2-й пары скобок, поэтому $2 опять получает значение undefined, которое наслаивается на предыдущее, а это неправильно. Квантификатор + отдаёт 1 букву, мы захватываем 1-м экз. 2-й пары скобок символ a, затем выходим за скобку. Тут надо сформировать $2 и т.д., но, видимо, здесь механизм об-ки re видит, что $2 уже имеет значение, поэтому вместо этого отменяется предыдущее значение undefined для $2, в результате $2 опять получает значение undefined, что и выводится, а надо вывести a. Вот так мне кажется... === Я ещё поумал и вижу, что я ошибался: когда \w+ отдаёт символ b, то мы не заходим левее открывающей скобки в выражении (\w+), поэтому $2 остаётся неопределённым, что и печатается. === Опа, я через неск. часов после этого опять раза 2 подумал и пришёл к выводу, что $2=undefined интуитивно должно быть ошибочно, правильно всё-таки выводить $2=a, как я раньше писал. Проверил почту - вот это да, пришёл ответ от гуру рег. выражений Дж. Фридла, который живёт на regex.info. (В сети есть 3-е издание его книги "Регулярные выражения"). Он написал: Hi Serge, I've been thinking about this for a while, and as far as I can tell it does seem to be a bug. By definition, $2 must be defined before the (?{...}) can run. It's probably a problem with how it backtracks. I'd suggest filing a bug report.. Поначалу я раздвоил это выражение ввиду квантификатора {2}, получилось такое: ((\w+)(?{print...}))((\w+)(?{print...})) (Здесь имеем в виду, что вторая копия также производит $1 и $2). Но так рассуждать неправильно, реально ничего не раздваивается. Вот почему выводится $2=undefined, по моему мнению: вначале (\w+) захватывает всё и выводится, что $2=ab. Далее выходим на модификатор {2}. Текущая позиция отмечена через |: (\w+)) | {2} Видим, что повтор \w не совпадает. Делаем бэктрекинг и по пути входим справа за закрывающую скобку: (\w+ | ) Видимо, в этом случае движок делает $2 undefined, а почему? Интуитивно кажется, что это надо делать только, когда мы выходим левее соответствующей открывающей скобки. Но я сомневаюсь, что авторы движка станут это исправлять: это вопрос идеологии работы движка, и ведь тогда теоретически некоторые старые программы могут начать не так работать... |
| Автор: MasterOfRegex 27.2.2011, 09:52 |
| Народ, который занимается разработкой Перла, окончательно признал, что "This *is* a bug. An optimization bug probably. In the PLUS regop." И вот интересный пример такого человека, который написал вышеприведённые слова: заменим \w+ на (?:\w|z)+ и потом на (?:\w|zz)+, получим разный вывод: 'ab' =~ /(((?:\w|z)+)(?{print defined $2 ? "\$2=$2\n" : "\$2 not defined\n"})){2}/; $2=ab $2 not defined $2=b 'ab' =~ /(((?:\w|zz)+)(?{print defined $2 ? "\$2=$2\n" : "\$2 not defined\n"})){2}/; $2=ab $2=a $2=b Кстати, я на днях засабмитил ещё один багрепорт, связанный с неправильной работой операторов \L, \l, \U и \u. Эти операторы, в отличие от функций lc, lcfirst, uc и ucfirst, выполняются слева направо: print "\L\udD\n"; # Dd верно print "\LdD\udD\n"; # dddd уже неверно! А если рядом поставить \U и \L (4 комбинации), то вообще будет кошмар: ошибка синтаксиса: print "\U\La\n"; Сергей (www.cronc.com/ru.shtml) |