Изменения

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

Устранение левой рекурсии

8661 байт добавлено, 22:35, 11 февраля 2018
м
Ошибка в примере устранения непосредственной левой рекурсии. Смотри 3-ий пункт процедуры устранения. Порождается ещё и строка вида A'->a
__TOC__
{{Определение
|definition=Говорят, что [[Контекстно-свободные грамматики, вывод, лево- и правосторонний вывод, дерево разбора|контекстно-свободная(к.с.КС) грамматика]] <tex>\Gamma</tex> содержит '''непосредственную левую рекурсию'''(англ. ''direct left recursion''), если она содержит правило вида <tex>A \rightarrow to A\alpha</tex>.
}}
{{Определение
|definition=Говорят, что к.с. КС-грамматика <tex>\Gamma</tex> содержит '''левую рекурсию''' (англ. ''left recursion''), если в ней существует вывод вида <tex>A \Rightarrow^* A\alpha</tex>.
}}
==Алгоритм устранения левой рекурсии==Приведем алгоритм, позволяющий для к.[[Методы трансляции#Нисходящий разбор|Методы нисходящего разбора]] не в состоянии работать слеворекурсивными грамматиками. грамматики ''без Проблема в том, что продукция вида <tex> A \varepsilon </tex>-правил'' построить эквивалентную ей к.с. грамматику (без <tex> Rightarrow^* A\varepsilon alpha</tex>-правил)может применяться бесконечно долго, так и не содержащую левой рекурсиивыработав некий терминальный символ, который можно было бы сравнить со строкой. Поэтому требуется преобразование грамматики, которое бы устранило левую рекурсию.
Для произвольной грамматики <tex>\Gamma</tex> левую рекурсию можно устранить следующим образом:#Воспользоваться [[Удаление_eps-правил_из_грамматики | алгоритмом удаления <tex> \varepsilon </tex>-правил]]. Получим грамматику без <tex> \varepsilon </tex>-правил для языка <tex>L(\Gamma) \setminus \lbrace \epsilon \rbrace</tex>#Воспользоваться алгоритмом устранения левой рекурсии#Если <tex>\epsilon</tex> присутствовал в языке исходной грамматики, добавить новый начальный символ <tex>S'</tex> и правила <tex>S' \rightarrow S \, | \, \epsilon </tex> ===Устранение непосредственной левой рекурсии===Опишем процедуру, устраняющую все правила вида <tex>A \rightarrow to A\alpha</tex> , для фиксированного нетерминала <tex>A</tex>.
<ol>
<li>Запишем все правила вывода из <tex>A</tex> в виде: <tex>A \rightarrow to A\alpha_1\,|\,mid \ldots\,|\,mid A\alpha_n\,|\,mid \beta_1\,|\,mid \ldots\,|\,mid \beta_m </tex>, где
<ul>
<li> <tex>\alpha</tex> {{- --}} непустая последовательность терминалов и нетерминалов (<tex>\alpha \ne nrightarrow \epsilon varepsilon </tex>);</li><li> <tex>\beta</tex> {{--- }} непустая последовательность терминалов и нетерминалов, не начинающаяся с <tex>A</tex>.</li>
</ul>
<li>Заменим правила вывода из <tex>A</tex> на: <tex>A \rightarrow to\beta_1A^\prime\, |\, mid \ldots\, |\, mid \beta_mA^\prime \,|\, mid \beta_1 \,|\, mid \ldots \,|\, mid \beta_m</tex> .</li>
<li>И создадим Создадим новый нетерминал <tex>{A^\prime } \rightarrow to \alpha_1Aalpha_1{A^\prime} \, |\, mid \ldots\, |mid \, \alpha_nAalpha_n{A^\prime | } \mid \alpha_1\, |\, mid \ldots\, |\, mid \alpha_n</tex> . </li>
</li>
</ol>
===Устранение произвольной левой рекурсии===Пусть множество всех нетерминалов Изначально нетерминал <tex>A</tex> порождает строки вида <tex>N = \lbrace A_1, A_2, beta\alpha_{i0}\alpha_{i1} \ldots , A_n \rbracealpha_{ik}</tex><div> for i = 1 to n { for j = 1 to i – 1 { рассмотреть все правила вывода из . В новой грамматике нетерминал <tex>A_jA</tex>: порождает <tex>A_j \rightarrow beta{A^\delta_1 | \ldots | \delta_kprime}</tex> заменить каждое правило , а <tex>A_i A^\rightarrow A_j \gammaprime</tex> на порождает строки вида <tex>A_i \rightarrow alpha_{i0}\delta_1\gamma | alpha_{i1} \ldots | \delta_k\gamma</tex> alpha_{ik} устранить непосредственную левую рекурсию для <tex>A_i</tex>. Из этого очевидно, что изначальная грамматика эквивалентна новой. }</div>===Пример===Инвариант: после <tex>j</tex> итераций внутреннего цикла для <tex>iA \to S\alpha \mid A\alpha</tex>*для <tex>k < i</tex> правые части правил вывода из <tex>A_k</tex> не начинаются с <tex>A_1, A_2, S \to A\ldots , A_kbeta</tex>*правые части правил вывода из Есть непосредственная левая рекурсия <tex>A_iA \to A\alpha</tex> не начинаются с . Добавим нетерминал <tex>A_1, A_2, A^\ldots , A_jprime</tex>*правые части правил вывода не начинаются с добавленных алгоритмом нетерминалов и добавим правила <tex>A_k A \to S\alpha{A^{\prime}}</tex>*грамматика не содержит ε-правил(проверяется индукцией по парам , <tex>(i,j)A^{\prime} \to \alpha{A^{\prime}} </tex>).
Таким образом, после применения алгоритма все правила вывода имеют вид*<tex>A \rightarrow c \alpha </tex>, где <tex>c</tex> - терминал, <tex>A</tex> - произвольный нетерминал*<tex>A_i \rightarrow A_j \alpha </tex>, где <tex>i < j</tex>, <tex>A_i , A_j</tex> - нетерминалы из исходной грамматики*<tex>A_i^{\prime} \rightarrow A_j \alpha </tex>, где <tex>A_i^{\prime}</tex> - новый нетерминал, <tex>A_j</tex> - нетерминал из исходной грамматикиНовая грамматика:
Если теперь перенумеровать нетерминалы, сохранив порядок для старых и присвоив всем новым меньшие номера, то все правила будут иметь вид*<tex>B_i A \rightarrow c to S\alpha </tex>, где <tex>c</tex> - терминал*<tex>B_i {A^{\prime}} \rightarrow B_j mid S\alpha </tex>, где <tex>i < j</tex>
<tex>A^{\prime} \to \alpha{A^{\prime} \mid \alpha}</tex> <tex>S \to A\beta</tex> В новой грамматике нет непосредственной левой рекурсии, но нетерминал <tex>A</tex> леворекурсивен, так как есть <tex> A \Rightarrow S\alpha{A^{\prime}} \Rightarrow A\beta\alpha{A^{\prime}} </tex> ==ЛитератураАлгоритм устранения произвольной левой рекурсии==Воспользуемся [[Удаление_eps-правил_из_грамматики | алгоритмом удаления <tex> \varepsilon </tex>-правил]]. Получим грамматику без <tex> \varepsilon </tex>-правил для языка <tex>L(\Gamma) \setminus \lbrace \varepsilon \rbrace</tex>. Упорядочим нетерминалы, например по возрастанию индексов, и будем добиваться того, чтобы не было правил вида <tex>A_i \to A_j\alpha</tex>, где <tex>j \leqslant i</tex>.Если данное условие выполняется для всех <tex>A_i</tex>, то в грамматике нет <tex>A_i \Rightarrow^* A_i</tex>, а значит не будет левой рекурсии.  Пусть <tex>N = \lbrace A_1, A_2, \ldots , A_n \rbrace</tex> {{---}} упорядоченное множество всех нетерминалов.  '''for''' <tex>A_i \in N</tex> '''for''' <tex>A_j \in \{ N \mid 1 \leqslant j < i \}</tex> '''for''' <tex>p \in \{ P \mid A_i \to A_j\gamma \}</tex> удалить продукцию <tex>p</tex> '''for''' <tex>Q \to x_i \in \{A_j \to \delta_1 \mid \ldots \mid \delta_k\}</tex> добавить правило <tex>A_i \to x_i\gamma</tex> устранить непосредственную левую рекурсию для <tex>A_i</tex> Если <tex>\varepsilon</tex> присутствовал в языке исходной грамматики, добавим новый начальный символ <tex>S'</tex> и правила <tex>S' \to S \, \mid \, \varepsilon </tex>. После <tex>i</tex> итерации внешнего цикла в любой продукции внешнего цикла в любой продукции вида <tex>A_k \to A_l\alpha, k < i</tex>, должно быть <tex>l > k</tex>. В результате при следующей итерации внутреннего цикла растет нижний предел <tex>m</tex> всех продукций вида <tex>A_i \to A_m\alpha</tex> до тех пор, пока не будет достигнуто <tex>i \leqslant m </tex>. После <tex>i</tex> итерации внешнего цикла в грамматике будут только правила вида <tex>A_i \to A_j\alpha</tex>, где <tex>j > i</tex>.Можно заметить, что неравенство становится строгим только после применения алгоритма устранения непосредственной левой рекурсии. При этом добавляются новые нетерминалы. Пусть <tex>{A^\prime}_i </tex> новый нетерминал. Можно заметить, что нет правила вида <tex>\ldots \to {A^\prime}_i</tex>, где <tex>{A^\prime}_i</tex> самый левый нетерминал, а значит новые нетерминалы можно не рассматривать во внешнем цикле. Для строгого поддержания инвариантов цикла можно считать, что созданный на <tex>i</tex> итерации в процессе устранения непосредственной левой рекурсии нетерминал имеет номер <tex>A_{-i}</tex> (т.е. имеет номер, меньший всех имеющихся на данный момент нетерминалов). На <tex>i</tex> итерации внешнего цикла все правила вида <tex>A_i \to A_j \gamma</tex> где <tex> j < i </tex> заменяются на <tex>A_i \to \delta_1\gamma \mid \ldots \mid \delta_k\gamma</tex> где <tex>A_j \to \delta_1 \mid \ldots \mid \delta_k</tex>. Очевидно, что одна итерация алгоритма не меняет язык, а значит язык получившийся в итоге грамматики совпадает с исходным. ===Оценка времени работы===Пусть <tex>a_i</tex> количество правил для нетерминала <tex>A_i</tex>.Тогда <tex>i</tex> итерация внешнего цикла будет выполняться за <tex>O\left(\sum\limits_{A_i \to A_j, j < i} a_j\right) + O(a_i)</tex>, что меньше чем <tex>O\left(\sum\limits_{i=1}^n a_j\right)</tex>, значит асимптотика алгоритма <tex>O\left(n\sum\limits_{i=1}^n a_j\right)</tex>. ===Худший случай===Проблема этого алгоритма в том, что в зависимости от порядка нетерминалов в множестве размер грамматки может получиться экспоненциальным. Пример грамматики для которой имеет значение порядок нетерминалов <tex>A_1 \to 0 \mid 1</tex> <tex>A_{i+1} \to {A_i}0 \mid {A_i}1 </tex> для <tex>1 \leqslant i < n</tex> Упорядочим множество нетерминалов по возрастанию индексов. Легко заметить, что правила для <tex>A_i</tex> будут представлять из себя все двоичные вектора длины <tex>i</tex>, а значит размер грамматики будет экспоненциальным.===Порядок выбора нетерминалов==={{Определение|definition=Говорят, что нетерминал <tex>X</tex> {{---}} '''прямой левый вывод''' (англ. ''direct left corner'') из <tex>A</tex>, если существует правило вида <tex>A \to X\alpha</tex>.}} {{Определение|definition='''Левый вывод''' (англ. ''left corner'') {{---}} [[Транзитивное_отношение|транзитивное]], [[Рефлексивное_отношение|рефлексивное]] замыкание отношения «быть прямым левым выводом».}} Во внутреннем цикле алгоритма для всех нетерминалов <tex>A_i</tex> и <tex>A_j</tex>, таких что <tex>j < i</tex> и <tex>A_j</tex> {{---}} прямой левый вывод из <tex>A_i</tex> заменяем все прямые левые выводы <tex>A_j</tex> из <tex>A_i</tex> на все выводы из <tex>A_j</tex>. Это действие удаляет левую рекурсию только если <tex>A_i</tex> {{---}} леворекурсивный нетерминал и <tex>A_j</tex> содержится в выводе <tex>A_i</tex> (то есть <tex>A_i</tex> {{---}} левый вывод из <tex>A_j</tex>,в то время как <tex>A_j</tex> {{---}} левый вывод из <tex>A_i</tex>). Перестанем добавлять бесполезные выводы, которые не участвуют в удалении левой рекурсии, упорядочив нетерминалы так: если <tex>j < i</tex> и <tex>A_j</tex> {{---}} прямой левый вывод из <tex>A_i</tex>, то <tex>A_i</tex> {{---}} левый вывод из <tex>A_j</tex>.Упорядочим их по уменьшению количества различных прямых левых выводов из них. Так как отношение «быть левым выводом» транзитивно,то если <tex>C</tex> {{---}} прямой левый вывод из <tex>B</tex>, то каждый левый вывод из С также будет левым выводом из <tex>B</tex>. А так как отношение «быть левым выводом» рефлексивно, <tex>B</tex> явлеяется своим левым выводом, а значит если <tex>C</tex> {{---}} прямой левый вывод из <tex>B</tex> {{---}} он должен следовать за <tex>B</tex> в упорядоченном множестве, если только <tex>B</tex> не является левым выводом из <tex>C</tex>. ==Пример==Дана грамматика: <tex>A \to S\alpha </tex> <tex>S \to S\beta \mid A\gamma \mid \beta</tex> Среди правил <tex>A</tex> непосредственной рекурсии нет, поэтому во время первой итерации внешнего цикла ничего не происходит. Во время второй итерации внешнего цикла правило <tex> S \to A\gamma </tex> переходит в <tex> S \to S\alpha\gamma </tex>.  Грамматика имеет вид  <tex>A \to S\alpha </tex> <tex>S \to{S}{\beta} \mid {S}{\alpha}{\gamma} \mid \beta</tex> Устраняем левую рекурсию для <tex>S</tex> <tex> S \to\beta{S_1}</tex> <tex> {S_1} \to\beta{S_1} \mid \alpha\gamma{S_1} \mid {\beta} \mid {\alpha}{\gamma} </tex> == См. также ==* [[Контекстно-свободные_грамматики,_вывод,_лево-_и_правосторонний_вывод,_дерево_разбора|Контекстно-свободные грамматики]]* [[Нормальная_форма_Хомского|Нормальная форма Хомского]]* [[Удаление_eps-правил_из_грамматики | Удаления <tex> \varepsilon </tex>-правил из грамматики]] == Источники информации ==
* ''Хопкрофт Д., Мотвани Р., Ульман Д.'' — '''Введение в теорию автоматов, языков и вычислений''', 2-е изд. : Пер. с англ. — Москва, Издательский дом «Вильямс», 2002. — 528 с. : ISBN 5-8459-0261-4 (рус.)
* ''Robert C. Moore'' — [http://aclweb.org/anthology-new/A/A00/A00-2033.pdf Removing Left Recursion from Context-Free Grammars ]
 
[[Категория: Теория формальных языков]]
[[Категория: Контекстно-свободные грамматики]]
[[Категория: Нормальные формы КС-грамматик]]
1
правка

Навигация