Модераторы: ginnie
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Объясните вывод re, Непонятный вывод из re 
:(
    Опции темы
MasterOfRegex
Дата 16.2.2011, 12:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 17
Регистрация: 25.10.2010

Репутация: нет
Всего: нет



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 символа.

Сергей
 Статьи по Perl регулярным выражениям
PM MAIL   Вверх
alezzz
Дата 16.2.2011, 21:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


сплю...
**


Профиль
Группа: Участник
Сообщений: 499
Регистрация: 17.8.2009

Репутация: 11
Всего: 14



Не претендую на гениальность, но мысль такая:
Нужно получить из выражения (\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
PM MAIL   Вверх
MasterOfRegex
Дата 17.2.2011, 10:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 17
Регистрация: 25.10.2010

Репутация: нет
Всего: нет



 Здесь наличие возвратов не должно играть роли, т.к. 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 на псевдокоде и его работа, из которой видно, что вторая пара скобок закрывается, когда текущая позиция в строке находится в её конце... Как же в этом случае можно идти дальше за скобку, непонятно...
 Это похоже на какую-то ошибку... Попробую написать на [email protected].

Сергей

===
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

 Хм, вроде я понял, ошибок тут, похоже, нет, зря гуру с [email protected] беспокоил...
 Undefined выводится, когда предыдущий захват 2-й парой скобок откачен, а нового захвата ещё не было. Хотя в re мы находимся за 2-й парой скобок. Вот так ставить квантификаторы к захватывающим скобкам... ;-\

Это сообщение отредактировал(а) MasterOfRegex - 17.2.2011, 11:37
PM MAIL   Вверх
alezzz
Дата 17.2.2011, 14:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


сплю...
**


Профиль
Группа: Участник
Сообщений: 499
Регистрация: 17.8.2009

Репутация: 11
Всего: 14



Цитата(MasterOfRegex @  17.2.2011,  10:54 Найти цитируемый пост)
Undefined выводится, когда предыдущий захват 2-й парой скобок откачен, а нового захвата ещё не было.

Ну а я что сказал?! 
Цитата(MasterOfRegex @  17.2.2011,  10:54 Найти цитируемый пост)
print "Match: \$1=$1 \$2=$2" if 'abcd' =~
/^((\w+)
    (?{print defined $2 ? "\$2=$2\n" : "\$2 not defined\n"})
  ){2}$
/x;

Все правильно, один возврат для второго \w+, один undef

Якорь $ можно убрать, \w+ захватывает до конца строки.


Это сообщение отредактировал(а) alezzz - 17.2.2011, 14:06
PM MAIL   Вверх
MasterOfRegex
Дата 17.2.2011, 19:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 17
Регистрация: 25.10.2010

Репутация: нет
Всего: нет



Да и ^ тоже можно убрать.
Я над этим серьёзно ещё не думал, а сейчас подумал раза 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 - 19.2.2011, 18:12
PM MAIL   Вверх
MasterOfRegex
Дата 27.2.2011, 09:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 17
Регистрация: 25.10.2010

Репутация: нет
Всего: нет



Народ, который занимается разработкой Перла, окончательно признал, что "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)
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Perl: Регулярные выражения | Следующая тема »


 




[ Время генерации скрипта: 0.0480 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.