Автор Тема: Объясните работу re  (Прочитано 4217 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн Serg31416

  • Заглянувший
  • Новичок
  • *
  • Сообщений: 18
  • +0/-0
  • 0
    • Просмотр профиля
    • http://www.cronc.com/ru.shtml
Объясните работу re
« : 16 Февраля 2011, 12:19:38 »
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 символа.

Сергей
« Последнее редактирование: 16 Февраля 2011, 14:37:05 от NAS »
Успехов!
 Сергей cronc.com/ru.shtml

Оффлайн arto

  • Ветеран
  • *****
  • Сообщений: 699
  • +0/-0
  • 2
    • Просмотр профиля
Объясните работу re
« Ответ #1 : 16 Февраля 2011, 21:30:22 »
сравните:
# perl -le \'print "Match: \\$1=$1" if "abcde" =~ /^(?:(\\w\\w+)(?{print defined $2 ? "-$2-" : "--"})){2}$/g\'
--
--
--
--
Match: $1=de

Оффлайн Serg31416

  • Заглянувший
  • Новичок
  • *
  • Сообщений: 18
  • +0/-0
  • 0
    • Просмотр профиля
    • http://www.cronc.com/ru.shtml
Объясните работу re
« Ответ #2 : 17 Февраля 2011, 11:32:59 »
Там немного не так надо было выводить, а то не видно различий между undefined и null. Вот минимальный пример:

#!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

Если включить use re qw(debug), то видно, что 2-я пара скобок открывается и закрывается перед буквой b. В этом случае должны создаваться переменные $2, $+, $^N и обр. ссылка \\2 с определёнными значениями, а они все неопределены. Я написал на ...@perl.org, пусть разъяснят.
Хм, вроде я понял, ошибок тут, похоже, нет, зря гуру беспокоил...
 Undefined выводится, когда предыдущий захват 2-й парой скобок откачен, а нового захвата ещё не было. Хотя в re мы находимся за 2-й парой скобок. Вот так ставить квантификаторы к захватывающим скобкам... ;-\\

===

Я ещё поумал и вижу, что я ошибался: когда \\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, а почему? Интуитивно кажется, что это надо делать только, когда мы выходим левее соответствующей открывающей скобки.
Но я сомневаюсь, что авторы движка станут это исправлять: это вопрос идеологии работы движка, и ведь тогда теоретически некоторые старые программы могут начать не так работать...
« Последнее редактирование: 19 Февраля 2011, 18:08:33 от Serg31416 »
Успехов!
 Сергей cronc.com/ru.shtml

Оффлайн Serg31416

  • Заглянувший
  • Новичок
  • *
  • Сообщений: 18
  • +0/-0
  • 0
    • Просмотр профиля
    • http://www.cronc.com/ru.shtml
Объясните работу re
« Ответ #3 : 17 Февраля 2011, 20:01:50 »
Извините, почему-то исправляется не последнее, а предпоследнее моё сообщение, а потом форум не даёт его редактировать, поэтому оно раздвоилось.
========================================

Я над этим серьёзно ещё не думал, а сейчас подумал раза 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, а почему? Интуитивно кажется, что это надо делать только, когда мы выходим левее соответствующей открывающей скобки.
Но я сомневаюсь, что авторы движка станут это исправлять: это вопрос идеологии работы движка, и ведь тогда теоретически некоторые старые программы могут начать не так работать...
« Последнее редактирование: 19 Февраля 2011, 18:10:48 от Serg31416 »
Успехов!
 Сергей cronc.com/ru.shtml

Оффлайн Serg31416

  • Заглянувший
  • Новичок
  • *
  • Сообщений: 18
  • +0/-0
  • 0
    • Просмотр профиля
    • http://www.cronc.com/ru.shtml
Объясните работу re
« Ответ #4 : 27 Февраля 2011, 09:45:01 »
Народ, который занимается разработкой Перла, окончательно признал, что "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";
Успехов!
 Сергей cronc.com/ru.shtml

Оффлайн sapandosii

  • Новичок
  • *
  • Сообщений: 1
  • +0/-0
    • Просмотр профиля
Объясните работу re
« Ответ #5 : 01 Марта 2016, 13:10:43 »
ХА ХА, упасть и не встать!!!!!!!!!

 

Sitemap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28