Изменения

Перейти к: навигация, поиск

Регулярные выражения с обратными ссылками

3712 байт добавлено, 07:50, 6 июня 2018
Внесены правки
Пример: <tex>aba(ca)ba.\,</tex> В данном регулярном выражении представлена одна группа {{---}} <tex>(ca).</tex>
Каждой группе соответствует порядковый номер. Нумерация идёт слева направо: номеру группы соответствует порядковый номер открывающей круглой скобки этой группы в тексте регулярного выражения(исключая случаи, когда скобки являются частью синтаксической конструкции).
Пример: <tex>(ab(cd))(ef).</tex> Группа <tex>№1</tex> {{---}} <tex>(ab(cd)),\,</tex> группа <tex>№2</tex> {{---}} <tex>(cd),\,</tex> группа <tex>№3</tex> {{---}} <tex>(ef)</tex>
Пример использования: <tex>((1\,|\,0)^*)\backslash 1.\,</tex> Данное регулярное выражение будет задавать язык тандемных повторов. Несмотря на то, что он не является [[Регулярные языки: два определения и их эквивалентность|регулярным]], его можно представить с помощью механизма обратных ссылок.
Для повторного использования '''регулярного выражения''' группы используется обозначение <tex>(?n),\,</tex> где <tex>n</tex> {{---}} номер группы. Использование круглых скобок обусловленно тем, что <tex>?,</tex> как управляющий символ, уже используется.В данном случае круглые скобки следует воспринимать как общепринятое '''условное обозначение''' обратной ссылки; запись <tex>(?n)</tex> не задаёт группу. Например, в выражении <tex>(aba)(?1)(caba)(?2)\;</tex> ссылке <tex>(?2)</tex> будет соответствовать <tex>(caba),\,</tex> а не <tex>(?1).</tex>
Обратите внимание, что символы круглых скобок и обратной косой черты являются управляющими. Чтобы использовать их непосредственно как часть слова, их нужно [https://ru.wikipedia.org/wiki/Экранирование_символов экранировать].
Пример экранирования (в данном случае в качестве символа экранирования используется символ обратной косой черты): <tex>\backslash 1</tex> {{---}} обратная ссылка на первую группу, <tex>\backslash\backslash 1</tex> {{---}} слово, состоящее из символа обратной косой черты и единицы.
}}
==Примеры==
# Регулярное выражение <tex>(aba?)c(?1)\,</tex> породит язык <tex>L=\{abcab,abacab,abcaba,abacaba\}.\;</tex> Для сравнения, запишем эквивалентное регулярное выражение без использования механизма обратных ссылок: <tex>(aba?)c(aba?).</tex>
# <tex>(a^*)\backslash 1.\,</tex> Данное регулярное выражение будет допускать только слова, в которых количество букв <tex>a</tex> чётно.
# Выведем регулярное выражение для языка, состоящего из палиндромов фиксированной длины <tex>n=2\cdot m\,</tex> или <tex>\,n=2\cdot m+1</tex> над алфавитом <tex>\Sigma</tex>:
#* для чётного <tex>n</tex>: <tex>\;(a_1)(a_2)(a_3)\dotsc(a_m)\backslash m\dotsc\backslash 3\backslash 2\backslash 1;</tex>
#* для нечётного <tex>n</tex>: <tex>\;(a_1)(a_2)(a_3)\dotsc(a_m)a_{m+1}\backslash m\dotsc\backslash 3\backslash 2\backslash 1,\;</tex>
#: где <tex>a_i\in\Sigma</tex> – любой одиночный символдля всех <tex>i.</tex>
# Запишем выражение для языка <tex>L=b^kab^kab^ka,\,k>0.\,</tex> Данный язык не является ни регулярным, ни контекстно-свободным (по [[Лемма о разрастании для КС-грамматик|лемме о разрастании]]), то есть является [[Иерархия Хомского формальных грамматик|контекстно-зависимым]], но также легко представим с помощью обратных ссылок:
#: <tex>L=(bb^*a)\backslash 1\backslash 1</tex>.
#: <tex>L=(a(?1)?b).</tex>
#: Следущий за ссылкой <tex>(?1)</tex> знак вопроса обозначает использование группы <tex>0</tex> или <tex>1</tex> раз, то есть осуществление рекурсивного вызова или его окончание.
#: <tex>(?1)</tex> ссылается на первую группу {{---}} <tex>(a(?1)?b)</tex>, что равносильно рекурсивной зависимости:
#:::: <tex>(a(?1)?b)=</tex>
#::: <tex>=(a(a(?1)?b)?b)=</tex>
Используя ссылки на регулярные выражения, соответствующие нетерминалам <tex>B</tex> и <tex>C</tex>, можно представить первое правило:
<tex>A\rightarrow BC\leftrightarrow A=\rightarrow ((?1n_B)\,(?2n_C)),\,</tex> где <tex>1(?n_B)</tex> и <tex>2(?n_C)</tex> соответствуют нетерминалам <tex>B</tex> и <tex>C</tex>;
Второе и третье правила не требуют использования обратных ссылок:
<tex>A\rightarrow a\leftrightarrow A=\rightarrow (a);\\S\rightarrow\varepsilon\leftrightarrow S=\rightarrow (\varepsilon).</tex>
Если какому-то нетерминалу <tex>A</tex> соответствуют несколько регулярных выражений <tex>r_1, r_2, \dotsc, r_n</tex>, заменить их на одно: <tex>A=((r_1)\,|\,(r_2)\,|\,\dotsc\,|\,(r_n))\,</tex> (очевидно, что оно также будет соответствовать этому нетерминалу).
Регулярное выражение для данной КС-грамматики соответствует нетерминалу <tex>S,\,</tex> однако в нём могут встречаться ссылки на внешние {{---}} отличные от <tex>S</tex> {{---}} группы. Будем обрабатывать такие ссылки, используя метод [[Контекстно-свободные грамматики, вывод, лево- и правосторонний вывод, дерево разбора|левостороннего вывода]]. При обработке очередной ссылки:# если эта ссылка встречается впервые, вместо неё подставим соответствующее регулярное выражение и запомним номер его группы в текущем регулярном выражении;# иначе вместо этой ссылки подставим ссылку на соответствующую группу.  После соответствующих замен регулярное выражение для <tex>S</tex> будет искомым.
}}
Регулярные выражения с обратными ссылками имеют бо́льшую мощность по сравнению с обычными. С их помощью реализуются как регулярные языки, так и контекстно-свободные грамматики, а также некоторые контекстно-зависимые (например, язык тандемных повторов). ===Примеры Пример преобразования===
Рассмотрим следующую КС-грамматику:
<tex>S\rightarrow AcA\\AS\rightarrowdA\varepsilon\S\rightarrow cB\\AS\rightarrow BAeB\\BA\rightarrow ba\\B\rightarrow cb</tex>
Эквивалентным будет выражение <tex>(((b\,|\,c)\,(?1)?)?).</tex>
# Приведём её к нормальной форме Хомского:
#: <tex>S\rightarrow CA\\S\rightarrow DA\\S\rightarrow CB\\S\rightarrow EB\\A\rightarrow a\\B\rightarrow b\\C\rightarrow c\\D\rightarrow d\\E\rightarrow e</tex>
# Каждому нетерминалу поставим в соответствие свой номер:
#: <tex>S\leftrightarrow 1, A\leftrightarrow 2, B\leftrightarrow 3, C\leftrightarrow 4, D\leftrightarrow 5, E\leftrightarrow 6</tex>
# Каждое правило представим в виде регулярного выражения с обратными ссылками:
#: <tex>S\rightarrow ((?4)(?2))\\S\rightarrow ((?5)(?2))\\S\rightarrow ((?4)(?3))\\S\rightarrow ((?6)(?3))\\A\rightarrow (a)\\B\rightarrow (b)\\C\rightarrow (c)\\D\rightarrow (d)\\E\rightarrow (e)</tex>
# Объединим регулярные выражения, соответствующие одинаковым нетерминалам:
#: <tex>S\rightarrow (((?4)(?2))\,|\,((?5)(?2))\,|\,((?4)(?3))\,|\,((?6)(?3)))\\A\rightarrow (a)\\B\rightarrow (b)\\C\rightarrow (c)\\D\rightarrow (d)\\E\rightarrow (e)</tex>
# Избавимся от внешних ссылок в регулярном выражении для <tex>S</tex>:
{| class="wikitable" style="display: inline-table; white-space: nowrap; text-align: center;"
|+Пошаговый вывод
! № || Текущее регулярное выражение
|-
| 1. || <tex>(((\underline{\textbf{?4}})(\underline{?2}))\,|\,((\underline{?5})(\underline{?2}))\,|\,((\underline{?4})(\underline{?3}))\,|\,((\underline{?6})(\underline{?3})))\,</tex>
|-
| 2. || <tex>(((c)(\underline{\textbf{?2}}))\,|\,((\underline{?5})(\underline{?2}))\,|\,((\underline{?4})(\underline{?3}))\,|\,((\underline{?6})(\underline{?3})))\,</tex>
|-
| 3. || <tex>(((c)(a))\,|\,((\underline{\textbf{?5}})(\underline{?2}))\,|\,((\underline{?4})(\underline{?3}))\,|\,((\underline{?6})(\underline{?3})))\,</tex>
|-
| 4. || <tex>(((c)(a))\,|\,((d)(\underline{\textbf{?2}}))\,|\,((\underline{?4})(\underline{?3}))\,|\,((\underline{?6})(\underline{?3})))\,</tex>
|-
| 5. || <tex>(((c)(a))\,|\,((d)(?4))\,|\,((\underline{\textbf{?4}})(\underline{?3}))\,|\,((\underline{?6})(\underline{?3})))\,</tex>
|-
| 6. || <tex>(((c)(a))\,|\,((d)(?4))\,|\,((?3)(\underline{\textbf{?3}}))\,|\,((\underline{?6})(\underline{?3})))\,</tex>
|-
| 7. || <tex>(((c)(a))\,|\,((d)(?4))\,|\,((?3)(b))\,|\,((\underline{\textbf{?6}})(\underline{?3})))\,</tex>
|-
| 8. || <tex>(((c)(a))\,|\,((d)(?4))\,|\,((?3)(b))\,|\,((e)(\underline{\textbf{?3}})))\,</tex>
|-
| 9. || <tex>(((c)(a))\,|\,((d)(?4))\,|\,((?3)(b))\,|\,((e)(?8)))\,</tex>
|}
{| class="wikitable" style="display: inline-table; white-space: nowrap; text-align: center;"
|+№ группы в <tex>S</tex>
! <tex>S</tex> || <tex>A</tex> || <tex>B</tex> || <tex>C</tex> || <tex>D</tex> || <tex>E</tex>
|-
| 1 || || || style="background: #1b5de2; color: white;" | || ||
|-
| 1 || style="background: #1b5de2; color: white;" | || || <b>3</b> || ||
|-
| 1 || <b>4</b> || || 3 || style="background: #1b5de2; color: white;" | ||
|-
| 1 || style="background: #1b5de2; color: white;" | 4 || || 3 || <b>6</b> ||
|-
| 1 || 4 || || style="background: #1b5de2; color: white;" | 3 || 6 ||
|-
| 1 || 4 || style="background: #1b5de2; color: white;" | || 3 || 6 ||
|-
| 1 || 4 || <b>8</b> || 3 || 6 || style="background: #1b5de2; color: white;" |
|-
| 1 || 4 || style="background: #1b5de2; color: white;" | 8 || 3 || 6 || <b>10</b>
|-
| 1 || 4 || 8 || 3 || 6 || 10
|}
'''Напоминание''': круглые скобки в записи обратной ссылки являются синтаксической конструкцией и не задают группу.
Другой пример:
<tex>S\rightarrow AB\\S\rightarrow\varepsilon\\A\rightarrow SS\\B\rightarrow CD\\C\rightarrow c\\D\rightarrow d</tex> Допустим, группа <tex>№1</tex> соответствует нетерминалу <tex>S,\Таким образом,</tex> группы <tex>№2</tex> и <tex>№5</tex> {{---}} нетерминалам <tex>A</tex> и <tex>D</tex> соответственно. # Для каждого нетерминала составим регулярное выражение:#для данной грамматики будет выглядеть так: <tex>S\leftrightarrow ((?2)(?3))\\S\leftrightarrow\varepsilon\\A\leftrightarrow ((?1)(?1))\\B\leftrightarrow ((?4)(?5))\\C\leftrightarrow c\\D\leftrightarrow d</tex># Объединим регулярные выражения, соответствующие одинаковым нетерминалам:#: <tex>S\leftrightarrow (((?2)(?3a))\,|\,\varepsilon)\\A\leftrightarrow ((?1)(?1)d)\\B\leftrightarrow ((?4)(?5))\\C\leftrightarrow c\\D\leftrightarrow d</tex># Искомое регулярное выражение соответствует нетерминалу <tex>S,\,</tex> однако оно ссылается на группы, отличные от <tex>S.</tex> Будем рекурсивно заменять такие ссылки на сами регулярные выражения до тех пор, пока в исходном регулярном выражении не останутся только терминалы и ссылки на стартовый нетерминал <tex>S</tex>:#: <tex>(((?2)(?3))\,|\,\varepsilon)\rightarrow(((?1)(?1)(?3))\,|\,\varepsilon)\rightarrow(((?1)(?1)(?4)(?5b))\,|\,\varepsilon)\rightarrow(((?1e)(?1)c(?58))\,|\,\varepsilon)\rightarrow(((?1)(?1)cd)\,|\,\varepsilon).</tex> Таким образом, регулярное выражение для этой грамматики будет выглядеть так: <tex>(((?1)(?1)cd)\,|\,\varepsilon).</tex>
==Применение==
Регулярные выражения с обратными ссылками имеют бо́льшую мощность по сравнению с обычными. С их помощью обратных ссылок можно составить реализуются как регулярные выражения для языка тандемных повторов языки, так и других языковконтекстно-свободные грамматики, а также некоторые контекстно-зависимые (например, где требуется «запоминать» части входящих в язык словтандемных повторов).
Регулярные выражения в языках программирования зачастую поддерживают обратные ссылки. На практике их можно использовать, например, для парсинга <tex>html</tex>-выражений (поиск подстрок, содержащихся в определённых тегах).
32
правки

Навигация