Чтобы определить то, что называют
Пусть фиксирована КС-
В этой и следующей лекции нас будет интересовать такой
вопрос: дана КС-
Пример 1. E ). Начальный символ: E. Правила:$$\begin{align*}
\hbox{\texttt{E}} \to \hbox{\texttt{(E)}}\\
\hbox{\texttt{E}} \to \hbox{\texttt{[E]}}\\
\hbox{\texttt{E}} \to \hbox{\texttt{EE}}\\
\hbox{\texttt{E}} \to
\end{align*}$$
(в последнем правиле справа стоит пустое слово).
Примеры выводимых слов:$$\begin{center}\ttfamily
\hspace*{1em} \textrm{(пустое слово)}\\
()\\
([$\,$])\\
()[([$\,$])]\\
\leavevmode \hbox{\texttt{[()[$\,$]()[$\,$]]}}
\end{center}$$
Примеры невыводимых слов:$$\begin{center}\ttfamily
(\\
)(\\
(]\\
([)]
\end{center}$$
Эта
Пример 2. Другая
( ) [ ] T E
Правила:$$\begin{align*}
\hbox{\texttt{E}} \to\\ \hbox{\texttt{E}} \to\hbox{\texttt{TE}}\\ \hbox{\texttt{T}} \to\hbox{\texttt{(E)}}\\ \hbox{\texttt{T}} \to\hbox{\texttt{[E]}}
\end{align*}$$
Начальным символом во всех приводимых далее примерах будем
считать символ, стоящий в левой части первого правила (в данном
случае это символ E ), не оговаривая этого особо.
Для каждого нетерминального символа можно рассмотреть
множество всех слов из T и E соответственно. Тогда правилам
E $$\to$$ |
E содержит пустое слово |
E-> |
если слово A принадлежит T,
а слово B принадлежит E, то слово AB
принадлежит E |
T->[E] |
если A принадлежит E, то слово [A] принадлежит T |
T->(E) |
если A принадлежит E, то слово (A) принадлежит T |
Сформулированные
15.1.1.
Сформулировать точно и доказать это утверждение для
произвольной
15.1.2.
Построить
(а) 00..0011..11 (число нулей равно числу единиц);
(б) 00..0011..11 (число нулей вдвое больше числа единиц);
(в) 00..0011..11 (число нулей больше числа единиц);
(и только они).
15.1.3.
Доказать, что не существует КС-00..0011..1122..22,
в которых числа нулей, единиц и двоек равны, и только
они.
Указание.
Доказать следующую лемму о произвольной
КС-
Пример 3. x, либо выражение в скобках.
15.1.4.
Привести пример другой
Ответ. Вот один из вариантов:$$\begin{align*} \langle{выр}\rangle \to\langle{выр}\rangle\ \hbox{\texttt{+}}\ \langle{выр}\rangle\\ \langle{выр}\rangle \to\langle{выр}\rangle\ \hbox{\texttt{*}}\ \langle{выр}\rangle\\ \langle{выр}\rangle \to \hbox{\texttt{x}}\\ \langle{выр}\rangle \to \hbox{\texttt{(}}\ \langle{выр}\rangle\ \hbox{\texttt{)}} \end{align*}$$
Эта
15.1.5.
Дана произвольная КС-
Решение. Заметим, что требование полиномиальности исключает
возможность решения, основанном на переборе всех возможных
выводов. Тем не менее полиномиальный алгоритм существует.
Поскольку практического значения он не имеет (используемые на
практике КС-
(1) Пусть в
Чтобы выполнить такое преобразование L и N выводится пустое слово, а из M
нет, то это правило надо заменить на правила$$\begin{align*}
\hbox{\texttt{K}}' \to
\hbox{\texttt{L}}'\hbox{\texttt{M}}'\hbox{\texttt{N}}'\\
\hbox{\texttt{K}}' \to \hbox{\texttt{M}}'\hbox{\texttt{N}}'\\
\hbox{\texttt{K}}' \to \hbox{\texttt{L}}'\hbox{\texttt{M}}'\\
\hbox{\texttt{K}}' \to \hbox{\texttt{M}}'
\end{align*}$$
(2) Итак, мы свели дело к
(3) Теперь проверка принадлежности какого-либо слова языку,
порожденному
Пусть в K в левой части, нет. Мы хотим
узнать, выводится ли данное слово $$A$$ из K.
Это будет так в одном из случаев:
L ;M, $$C$$ выводится
из N, а $$D$$ выводится из L.Вся эта информация уже есть (слова $$B$$, $$C$$, $$D$$
короче $$A$$, а L рассмотрен до K ).
Легко видеть, что число действий этого алгоритма
полиномиально. Степень полинома зависит от числа нетерминалов
в правых частях правил и может быть понижена, если N - новый
15.1.6.
Рассмотрим K,
нетерминалами 1, 2, 3 и правилами$$\begin{align*}
\texttt{K} \to \texttt{0}\\
\texttt{K} \to \texttt{1 K}\\
\texttt{K} \to \texttt{2 K K}\\
\texttt{K} \to \texttt{3 K K K}
\end{align*}$$
Как проверить выводимость слова в этой
Решение. Хранится целая переменная n, n выводимых слов.
15.1.7.
Тот же вопрос для
В отличие от алгоритма предыдущего раздела (представляющего
чисто теоретический интерес), алгоритмы на основе
рекурсивного спуска часто используются на практике. Этот метод применим, однако, далеко не ко всем
Идея метода рекурсивного спуска такова. Для каждого
K мы строим процедуру ReadK, которая -
в применении к любому входному слову $$x$$ - делает две
вещи:
K слова;K.Прежде чем описывать этот метод более подробно, договоримся
о том, как процедуры получают сведения о входном слове и как
сообщают о результатах своей работы. Мы предполагаем, что буквы
входного слова поступают к ним по одной, т.е. имеется граница,
отделяющая "прочитанную" часть от (End Of , которая сдвигает границу
на один символ. (Она применима, если Next<>.) Пусть,
наконец, имеется булевская переменная b.
Теперь мы можем сформулировать наши требования к процедуре ReadK. Они состоят в следующем:
ReadK прочитывает из оставшейся части слова
максимальное начало $$A$$, являющееся началом некоторого слова, выводимого из K ;b становится K или лишь
невыводимым началом выводимого (из K ) слова.Для удобства введем такую терминологию: выводимое из K
слово будем называть K -словом, а любое начало любого выводимого из K слова - K -началом. Два сформулированных
требования вместе будем выражать словами " ReadK корректна для K ".
Начнем с примера. Пусть правило$$\begin{center}\ttfamily
K \to L M
\end{center}$$
является единственным правилом K
в левой части, пусть L, M - нетерминалы
и ReadL, ReadM - корректные (для них) процедуры.
Рассмотрим такую процедуру:
procedure ReadK; begin | ReadL; | if b then begin | | ReadM; | end; end;
15.2.1.
Привести пример, когда эта процедура будет
некорректной для K.
Ответ. Пусть из L выводится любое слово вида 00..00, а из M выводится лишь слово 01. Тогда
из K выводится слово 00001, но процедура ReadK
этого не заметит.
Укажем достаточные условия корректности процедуры ReadK. Для этого нам понадобятся некоторые обозначения.
Пусть фиксированы КС-Посл(N).
(Если никакое $$N$$ -слово не является собственным началом другого $$N$$ -слова, то множество Посл(N) пусто.)
15.2.2.
Указать (а) Посл(E) для примера 1 (см. пункт 15.1.);
(б) Посл(E) и Посл(T) для примера 2 (см. пункт 15.1.);
(в) $$Посл(\langle{слаг}\rangle)$$ и $$Посл(\langle{множ}\rangle)$$ для
примера 3 (см. пункт 15.1.);
Ответ. (а) Посл(E) = {[, (}. (б) Посл(E) = {[, (} ; Посл(T) пусто (никакое T -слово не является началом другого). (в) $$Посл(\langle{слаг}\rangle) = \{\hbox{\texttt{*}}\}$$ ; $$Посл(\langle{множ}\rangle)$$ пусто.
Кроме того, для каждого Нач(N ) множество всех терминалов, являющихся первыми буквами непустых $$N$$ -слов. Это обозначение - вместе с предыдущим - позволит дать достаточное условие
корректности процедуры ReadK в описанной выше ситуации.
15.2.3.
Доказать, что если Посл(L) не пересекается с Нач(M) и множество всех M -слов непусто, то ReadK корректна.
Решение. Рассмотрим два случая.
(1) Пусть после ReadL b ложно.
В этом случае ReadL читает со входа максимальное L -начало $$A$$, не являющееся L -словом. Оно является K -началом (здесь важно, что множество M -слов
непусто.). Будет ли оно максимальным K -началом среди
начал входа? Если нет, то $$A$$ является началом
слова $$BC$$, где $$B$$ есть L -слово, $$C$$ есть M -начало и $$BC$$ -
более длинное начало входа, чем $$A$$. Если $$B$$ длиннее $$A$$,
то $$A$$ - не максимальное начало входа, являющееся L -началом, что противоречит корректности ReadL.
Если $$B = A$$, то $$A$$ было бы L -словом, а это не так. Значит, $$B$$ короче $$A$$, $$C$$ непусто и первый символ слова $$C$$ следует в $$A$$ за последним символом
слова $$B$$, т.е. Посл(L) пересекается с Нач(M). Противоречие. Итак, $$A$$ максимально. Из сказанного следует также, что $$A$$ не является K -словом. Корректность процедуры ReadK в этом случае проверена.
(2) Пусть после ReadL b
ReadK начало
входа имеет вид $$AB$$, где $$A$$ есть L -слово, а $$B$$ есть M -начало. Тем самым $$AB$$ есть K -начало. Проверим
его максимальность. Пусть $$C$$ есть большее K -начало.
Тогда либо $$C$$ есть L -начало (что невозможно, так как $$A$$ было максимальным L -началом), либо $$C =
A'B'$$, где $$A'$$ - L -слово, $$B'$$ - M -начало. Если $$A'$$ короче $$A$$, то $$B'$$ непусто и начинается с символа, принадлежащего и Нач(M), и Посл(L), что невозможно.
Если $$A'$$ длиннее $$A$$, то $$A$$ - не максимальное L -начало.
Итак, $$A' = A$$. Но в этом случае $$B'$$ есть
продолжение $$B$$,
что противоречит корректности ReadM. Итак, $$AB$$ -
максимальное K -начало. Остается проверить правильность
выдаваемого процедурой ReadK значения переменной b.
Если оно M -слово, и надо проверить, что $$AB$$ - не K -слово. В самом деле, если бы выполнялось $$AB = A'B'$$, где $$A'$$ - L -слово, $$B'$$ - M -слово, то $$A'$$ не
может быть длиннее $$A$$ ( ReadL читает максимальное
слово), $$A'$$ не может быть равно $$A$$ (тогда $$B'$$ равно $$B$$
и не является M -словом) и $$A'$$ не может быть
короче $$A$$ (тогда первый символ $$B'$$ принадлежит и Нач(M), и Посл(L) ). Задача решена.
Перейдем теперь к другому частному случаю. Пусть
в КС-K нет.
15.2.4.
Считая, что ReadL, ReadM и ReadN корректны
(для L, M и N ) и что множества Нач(L), Нач(M) и Нач(N) не пересекаются, написать процедуру, корректную для K.
Решение. Схема процедуры такова:
procedure ReadK; begin | if (Next принадлежит Нач(L)) then begin | | ReadL; | end else if (Next принадлежит Нач(M)) then begin | | ReadM; | end else if (Next принадлежит Нач(N)) then begin | | ReadN; | end else begin | | b := true или false в зависимости от того, | | выводимо ли пустое слово из K или нет | end; end;
Докажем, что ReadK корректно реализует K. Если Next не принадлежит ни одному из множеств Нач(L), Нач(M), Нач(N),то пустое слово является наибольшим началом входа, являющимся K -началом. Если Next
принадлежит одному (и, следовательно, только одному) из этих
множеств, то максимальное начало входа, являющееся K -началом, непусто и читается соответствующей процедурой.
15.2.5. Используя сказанное, составить процедуру
распознавания выражений для
Решение. Эта
procedure ReadK; begin | ReadL; | if b then begin | | ReadM; | end; | if b then begin | | ReadN; | end; end;
Для ее корректности надо, чтобы Посл(L) не пересекалось с Нач(MN) (которое равно Нач(M), если из M не выводится пустое слово, и равно Нач(M) и Нач(N), если выводится), а также чтобы Посл(M) не пересекалось с Нач(N).
Аналогичным образом правила$$\begin{align*} \hbox{\texttt{K}}\to\hbox{\texttt{L M N}}\\ \hbox{\texttt{K}}\to\hbox{\texttt{P Q}}\\ \hbox{\texttt{K}}\to \end{align*}$$ приводят к процедуре
procedure ReadK; begin | if (Next принадлежит Нач(LMN)) then begin | | ReadL; | | if b then begin ReadM; end; | | if b then begin ReadN; end; | end else if (Next принадлежит Нач(PQ)) then begin | | ReadP; | | if b then begin ReadQ; end; | end else begin | | b := true; | end; end;
корректность которой требует, чтобы Нач(LMN) не
пересекалось с Нач(.
Читая приведенную далее программу, полезно иметь в виду соответствие между русскими и английскими словами:$$\begin{tabular}{ll} ВЫРажение EXPRession \\ ОСТаток ВЫРажения REST of EXPRession\\ СЛАГаемое ADDitive term\\ ОСТаток СЛАГаемого REST of ADDitive term\\ МНОЖитель FACTor \end{tabular}$$
procedure ReadSymb (c: Symbol);
| b := (Next = c);
| if b then begin
| | Move;
| end;
end;
procedure ReadExpr;
| ReadAdd;
| if b then begin ReadRestExpr; end;
end;
procedure ReadRestExpr;
| if Next = '+' then begin
| | ReadSymb ('+');
| | if b then begin ReadExpr; end;
| end else begin
| | b := true;
| end;
end;
procedure ReadAdd;
| ReadFact;
| if b then begin ReadRestAdd; end;
end;
procedure ReadRestAdd;
| if Next = '*' then begin
| | ReadSymb ('*');
| | if b then begin ReadAdd; end;
| end else begin
| | b := true;
| end;
end;
procedure ReadFact;
| if Next = 'x' then begin
| | ReadSymb ('x');
| end else if Next = '(' then begin
| | ReadSymb ('(');
| | if b then begin ReadExpr; end;
| | if b then begin ReadSymb (')'); end;
| end else begin
| | b := false;
| end;
end;
Осталось обсудить проблемы, связанные с взаимной рекурсивностью этих процедур (одна использует другую и наоборот). В K ничего не
выводится, Посл(K) и Нач(K) пусты, но написанная по нашим канонам процедура
procedure ReadK; begin | ReadK; | if b then begin | | ReadK; | end; end;
не заканчивает работы.)
В данном случае процедуры ReadRestExpr, ReadRestAdd, ReadFact либо завершаются, либо
уменьшают длину непрочитанной части входа. Поскольку любой цикл
вызовов включает одну из них, то зацикливание невозможно.
15.2.6.
Пусть в K
в левой части, имеющих вид$$\begin{align*}
\hbox{\texttt{K}}\to\hbox{\texttt{L K}}\\
\hbox{\texttt{K}}\to
\end{align*}$$
по которым K -слово представляет собой конечную
последовательность L -слов, причем множества Посл(L) и Нач(K) (в данном случае равное Нач(L) ) не пересекаются. Используя корректную для L процедуру ReadL, написать корректную для K процедуру ReadK, не используя
Решение. По нашим правилам следовало бы написать
procedure ReadK; begin | if (Next принадлежит Нач(L)) then begin | | ReadL; | | if b then begin ReadK; end; | end else begin | | b := true; | end; end;
Эта рекурсивная процедура
procedure ReadK; begin | b := true; | while b and (Next принадлежит Нач(L)) do begin | | ReadL; | end; end;
Формально можно проверить эту эквивалентность так. Завершаемость в обоих случаях ясна. Достаточно проверить поэтому, что тело рекурсивной процедуры
if (Next принадлежит Нач(L)) then begin | ReadL; | if b then begin | | b := true; | | while b and (Next принадлежит Нач(L)) do begin | | | ReadL; | | end; | end; end else begin | b := true; end;
Первую команду b:= можно выкинуть (в этом месте и так b
b := true; if (Next принадлежит Нач(L) then begin | ReadL; | if b then begin | | while b and (Next принадлежит Нач(L)) do begin | | | ReadL; | | end; | end; end;
Теперь внутренний if можно выкинуть (если b ложно,
цикл while все равно не выполняется) и добавить в условие
внешнего if условие b (которое все равно
b := true; if b and (Next принадлежит Нач(L)) then begin | ReadL; | while b and (Next принадлежит Нач(L)) do begin | | ReadL; | end; end;
что
15.2.7. Доказать корректность приведенной выше нерекурсивной программы непосредственно, без ссылок на рекурсивную.
Решение. Рассмотрим наибольшее начало входа,
являющееся K -началом. Оно представляется в виде
L -слов и, возможно, одного непустого L -начала, не являющегося L -словом. b $$\Leftrightarrow$$
(последнее прочитанное является L -словом).
Сохранение L -словом
(из числа оставшихся) идет символ из Нач(L), и потому это слово - максимальное начало входа, являющееся L -началом.
На практике при записи K имеют вид$$\begin{align*}
\hbox{\texttt{K}}\to \hbox{\texttt{L K}}\\
\hbox{\texttt{K}}\to
\end{align*}$$
(т.е. K -слова - это последовательности L -слов),
то этих правил не пишут, а вместо K пишут L в
Например, рассмотренная выше
15.2.8.
Написать процедуру, корректную для $$\langle{выр}\rangle$$, следуя этой
Решение.
procedure ReadSymb (c: Symbol);
| b := (Next = c);
| if b then begin Move; end;
end;
procedure ReadExpr;
begin
| ReadAdd;
| while b and (Next = '+') do begin
| | Move; ReadAdd;
| end;
end;
procedure ReadAdd;
begin
| ReadFact;
| while b and (Next = '*') do begin
| | Move; ReadFact;
| end;
end;
procedure ReadFact;
begin
| if Next = 'x' do begin
| | Move; b := true;
| end else if Next = '(' then begin
| | Move; ReadExpr;
| | if b then begin ReadSymb (')'); end;
| end else begin
| | b := false;
| end;
end;
15.2.9.
В последней процедуре команду b:= можно опустить.
Почему?
Решение. Можно предполагать, что все процедуры вызываются при b=.
В этом разделе мы рассмотрим еще один метод проверки
выводимости в КС-
Определение.
15.3.1. Для каждого выводимого слова (из терминалов) существует его левый вывод.
Решение. Различные нетерминалы заменяются независимо; если в процессе вывода появилось слово $$\ldots K\ldots L\ldots$$, где $$K$$, $$L$$ - нетерминалы, то замены $$K$$ и $$L$$
можно производить в любом порядке. Поэтому можно
перестроить вывод так, чтобы стоящий левее
15.3.2. В
Решение. На первом шаге можно применить только правило (2):$${\texttt{E}} \to {\texttt{TE}}$$
Что будет дальше с T? Так как слово $$A$$ начинается на [, то может примениться только правило (4):$${\texttt{E}} \to {\texttt{TE}}\to {\texttt{[E]E}}$$
Первое E должно замениться на (иначе вторым символом была бы скобка ] ):$${\texttt{E}} \to {\texttt{TE}}\to{\texttt{[E]E}} \to{\texttt{[TE]E}}$$
и T должно заменяться по (3):$${\texttt{E}}\to{\texttt{TE}}\to{\texttt{[E]E}} \to{\texttt{[TE]E}}\to{\texttt{[(E)E]E}}$$
Далее первое E должно замениться на пустое слово (иначе
третьей буквой слова будет ( или [ - только на
эти символы может начинаться слово, выводимое из T ):$${\texttt{E}}\to{\texttt{TE}}\to{\texttt{[E]E}}\to{\texttt{[TE]E}}\to{\texttt{[(E)E]E}}\to{\texttt{[()E]E}}$$
и далее$$\begin{multiline*}
\ldots\to \hbox{\texttt{[()TE]E}}
\to\hbox{\texttt{[()(E)E]E}}
\to\hbox{\texttt{[()(TE)E]E}}
\to\hbox{\texttt{[()([E]E)E]E}}\to{}\\
%
\to\hbox{\texttt{[()([\,]E)E]E}}
\to\hbox{\texttt{[()([\,])E]E}}
\to\hbox{\texttt{[()([\,])]E}}
\to\hbox{\texttt{[()([\,])]}}
\end{multiline*}$$
Что требуется от
Рассмотрим множество Нач(LMN)
тех терминалов, с которых начинаются непустые слова,
выводимые из $$LMN$$. (Это множество равно Нач(L), Нач(M), если из $$L$$ выводится пустое слово, а также с Нач(N), если из $$L$$ и из $$M$$ выводится пустое слово.) Чтобы описанный метод был применим, надо,
чтобы Нач(LMN), Нач( и Нач(R) не пересекались. Но этого мало. Ведь может быть так, например, что из $$LMN$$ будет выведено пустое слово, а из слова $$U$$ будет выведено слово, начинающееся на букву из Нач(. Следующие определения учитывают эту проблему.
Напомним, что определение
Для каждого слова $$X$$ из терминалов и нетерминалов
через Нач(X) обозначаем множество всех терминалов,
с которых начинаются непустые слова из терминалов,
выводимые из $$X$$. (В случае, если из любого Нач(X) слова только из терминалов или любые слова. Мы будем предполагать далее,
что это условие выполнено.)
Для каждого Послед(K) обозначим множество терминалов, которые встречаются в выводимых (в Посл(K) предыдущего раздела!) Кроме того, в Послед(K) включается символ , если существует выводимое слово, оканчивающееся на $$K$$.
Для каждого правила$$K \to V$$
(где $$K$$ -
Определение.
15.3.3.
Является ли
Решение. Нет: символ # принадлежит множествам направляющих символов для обоих правил (для второго - поскольку # принадлежит $$Послед(K)$$ ).
15.3.4.
Написать
Решение.$$\begin{align*} \hbox{\texttt{K}}\to\hbox{\texttt{\# K}}\\ \hbox{\texttt{K}}\to \end{align*}$$
Как говорят, "леворекурсивное" правило заменено на "праворекурсивное".
Следующая задача показывает, что для
15.3.5.
Пусть дано выводимое в .)
Решение. Эти буквы принадлежат направляющим множествам различных правил.
15.3.6.
Доказать, что если слово выводимо в
Решение. Предыдущая задача показывает, что на каждом шаге левый вывод продолжается однозначно.
15.3.7.
Решение. Пусть из $$K$$ выводится $$KU$$, где $$K$$ -
Таким образом, к
15.3.8.
Используя сказанное, построить алгоритм проверки выводимости слова из терминалов в
Решение. Мы следуем описанному выше методу поиска левого вывода, храня лишь часть слова, находящуюся правее уже
прочитанной части входного слова. Другими словами, мы храним
слово S из терминалов и нетерминалов, обладающее такими свойствами (прочитанную часть входа обозначаем через A ):
AS выводимо в ASЭти свойства вместе будем обозначать "(И)".
Вначале A пусто, а S состоит из единственного символа - начального
Если в некоторый момент S начинается на
терминал t и $${\texttt{t}} = {\texttt{Next}}$$, то
можно выполнить команду и удалить символ t,
являющийся начальным в S, поскольку при этом AS не
меняется.
Если S начинается на терминал t и $${\texttt{t}}\neq{\texttt{Next}}$$, то входное слово
невыводимо - ибо по условию любой его вывод должен проходить через AS. (Это же справедливо и в случае $${\texttt{Next}} =
{\texttt{EOI}}$$.)
Если S пусто, то из условия (И) следует, что входное слово
выводимо тогда и только тогда, когда $${\texttt{Next}} =
{\texttt{EOI}}$$.
Остается случай, когда S начинается с некоторого
K. По доказанному выше все левые выводы
из S слов, начинающихся на символ Next, начинаются
с применения к S одного и того же правила - того, для
которого Next принадлежит направляющему множеству. Если
таких правил нет, то входное слово невыводимо. Если такое
правило есть, то нужно применить его к первому символу
слова S - при этом свойство (И) не нарушится. Приходим
к такому алгоритму:
s := пустое слово;
error := false;
{error => входное слово невыводимо;}
{not error => (И)}
while (not error) and not ((Next=EOI) and (S пусто))
| | do begin
| if (S начинается на терминал, равный Next) then begin
| | Move; удалить из S первый символ;
| end else if (S начинается на терминал, не равный Next)
| | then begin
| | error := true;
| end else if (S пусто) and (Next <> EOI) then begin
| | error := true;
| end else if (S начинается на нетерминал и Next входит в
| | направляющее множество одного из правил для этого
| | нетерминала) then begin
| | применить это правило
| end else if (S начинается на нетерминал и Next не входит
| | в направляющее множество ни одного из правил для этого
| | нетерминала) then begin
| | error := true;
| end else begin
| | {так не бывает}
| end;
end;
{входное слово выводимо <=> not error}
Алгоритм заканчивает работу, поскольку при появлении
терминала в начале слова S происходит чтение со входа
или остановка, а бесконечный цикл сменяющих друг друга
нетерминалов в начале S означал бы, что
Замечания.
S как Next.
После этого на каждом шаге цикла будет читаться S, и небольшую программу,
выполняющую действия в соответствии с этой таблицей.15.3.9.
При проверке того, относится ли данная
Решение.Пусть, например, в Нач(T) и Послед(T).
Чтобы определить то, что называют
Пусть фиксирована КС-
В этой и следующей лекции нас будет интересовать такой
вопрос: дана КС-
Пример 1. E ). Начальный символ: E. Правила:$$\begin{align*}
\hbox{\texttt{E}} \to \hbox{\texttt{(E)}}\\
\hbox{\texttt{E}} \to \hbox{\texttt{[E]}}\\
\hbox{\texttt{E}} \to \hbox{\texttt{EE}}\\
\hbox{\texttt{E}} \to
\end{align*}$$
(в последнем правиле справа стоит пустое слово).
Примеры выводимых слов:$$\begin{center}\ttfamily
\hspace*{1em} \textrm{(пустое слово)}\\
()\\
([$\,$])\\
()[([$\,$])]\\
\leavevmode \hbox{\texttt{[()[$\,$]()[$\,$]]}}
\end{center}$$
Примеры невыводимых слов:$$\begin{center}\ttfamily
(\\
)(\\
(]\\
([)]
\end{center}$$
Эта
Пример 2. Другая
( ) [ ] T E
Правила:$$\begin{align*}
\hbox{\texttt{E}} \to\\ \hbox{\texttt{E}} \to\hbox{\texttt{TE}}\\ \hbox{\texttt{T}} \to\hbox{\texttt{(E)}}\\ \hbox{\texttt{T}} \to\hbox{\texttt{[E]}}
\end{align*}$$
Начальным символом во всех приводимых далее примерах будем
считать символ, стоящий в левой части первого правила (в данном
случае это символ E ), не оговаривая этого особо.
Для каждого нетерминального символа можно рассмотреть
множество всех слов из T и E соответственно. Тогда правилам
E $$\to$$ |
E содержит пустое слово |
E-> |
если слово A принадлежит T,
а слово B принадлежит E, то слово AB
принадлежит E |
T->[E] |
если A принадлежит E, то слово [A] принадлежит T |
T->(E) |
если A принадлежит E, то слово (A) принадлежит T |
Сформулированные
15.1.1.
Сформулировать точно и доказать это утверждение для
произвольной
15.1.2.
Построить
(а) 00..0011..11 (число нулей равно числу единиц);
(б) 00..0011..11 (число нулей вдвое больше числа единиц);
(в) 00..0011..11 (число нулей больше числа единиц);
(и только они).
15.1.3.
Доказать, что не существует КС-00..0011..1122..22,
в которых числа нулей, единиц и двоек равны, и только
они.
Указание.
Доказать следующую лемму о произвольной
КС-
Пример 3. x, либо выражение в скобках.
15.1.4.
Привести пример другой
Ответ. Вот один из вариантов:$$\begin{align*} \langle{выр}\rangle \to\langle{выр}\rangle\ \hbox{\texttt{+}}\ \langle{выр}\rangle\\ \langle{выр}\rangle \to\langle{выр}\rangle\ \hbox{\texttt{*}}\ \langle{выр}\rangle\\ \langle{выр}\rangle \to \hbox{\texttt{x}}\\ \langle{выр}\rangle \to \hbox{\texttt{(}}\ \langle{выр}\rangle\ \hbox{\texttt{)}} \end{align*}$$
Эта
15.1.5.
Дана произвольная КС-
Решение. Заметим, что требование полиномиальности исключает
возможность решения, основанном на переборе всех возможных
выводов. Тем не менее полиномиальный алгоритм существует.
Поскольку практического значения он не имеет (используемые на
практике КС-
(1) Пусть в
Чтобы выполнить такое преобразование L и N выводится пустое слово, а из M
нет, то это правило надо заменить на правила$$\begin{align*}
\hbox{\texttt{K}}' \to
\hbox{\texttt{L}}'\hbox{\texttt{M}}'\hbox{\texttt{N}}'\\
\hbox{\texttt{K}}' \to \hbox{\texttt{M}}'\hbox{\texttt{N}}'\\
\hbox{\texttt{K}}' \to \hbox{\texttt{L}}'\hbox{\texttt{M}}'\\
\hbox{\texttt{K}}' \to \hbox{\texttt{M}}'
\end{align*}$$
(2) Итак, мы свели дело к
(3) Теперь проверка принадлежности какого-либо слова языку,
порожденному
Пусть в K в левой части, нет. Мы хотим
узнать, выводится ли данное слово $$A$$ из K.
Это будет так в одном из случаев:
L ;M, $$C$$ выводится
из N, а $$D$$ выводится из L.Вся эта информация уже есть (слова $$B$$, $$C$$, $$D$$
короче $$A$$, а L рассмотрен до K ).
Легко видеть, что число действий этого алгоритма
полиномиально. Степень полинома зависит от числа нетерминалов
в правых частях правил и может быть понижена, если N - новый
15.1.6.
Рассмотрим K,
нетерминалами 1, 2, 3 и правилами$$\begin{align*}
\texttt{K} \to \texttt{0}\\
\texttt{K} \to \texttt{1 K}\\
\texttt{K} \to \texttt{2 K K}\\
\texttt{K} \to \texttt{3 K K K}
\end{align*}$$
Как проверить выводимость слова в этой
Решение. Хранится целая переменная n, n выводимых слов.
15.1.7.
Тот же вопрос для
В отличие от алгоритма предыдущего раздела (представляющего
чисто теоретический интерес), алгоритмы на основе
рекурсивного спуска часто используются на практике. Этот метод применим, однако, далеко не ко всем
Идея метода рекурсивного спуска такова. Для каждого
K мы строим процедуру ReadK, которая -
в применении к любому входному слову $$x$$ - делает две
вещи:
K слова;K.Прежде чем описывать этот метод более подробно, договоримся
о том, как процедуры получают сведения о входном слове и как
сообщают о результатах своей работы. Мы предполагаем, что буквы
входного слова поступают к ним по одной, т.е. имеется граница,
отделяющая "прочитанную" часть от (End Of , которая сдвигает границу
на один символ. (Она применима, если Next<>.) Пусть,
наконец, имеется булевская переменная b.
Теперь мы можем сформулировать наши требования к процедуре ReadK. Они состоят в следующем:
ReadK прочитывает из оставшейся части слова
максимальное начало $$A$$, являющееся началом некоторого слова, выводимого из K ;b становится K или лишь
невыводимым началом выводимого (из K ) слова.Для удобства введем такую терминологию: выводимое из K
слово будем называть K -словом, а любое начало любого выводимого из K слова - K -началом. Два сформулированных
требования вместе будем выражать словами " ReadK корректна для K ".
Начнем с примера. Пусть правило$$\begin{center}\ttfamily
K \to L M
\end{center}$$
является единственным правилом K
в левой части, пусть L, M - нетерминалы
и ReadL, ReadM - корректные (для них) процедуры.
Рассмотрим такую процедуру:
procedure ReadK; begin | ReadL; | if b then begin | | ReadM; | end; end;
15.2.1.
Привести пример, когда эта процедура будет
некорректной для K.
Ответ. Пусть из L выводится любое слово вида 00..00, а из M выводится лишь слово 01. Тогда
из K выводится слово 00001, но процедура ReadK
этого не заметит.
Укажем достаточные условия корректности процедуры ReadK. Для этого нам понадобятся некоторые обозначения.
Пусть фиксированы КС-Посл(N).
(Если никакое $$N$$ -слово не является собственным началом другого $$N$$ -слова, то множество Посл(N) пусто.)
15.2.2.
Указать (а) Посл(E) для примера 1 (см. пункт 15.1.);
(б) Посл(E) и Посл(T) для примера 2 (см. пункт 15.1.);
(в) $$Посл(\langle{слаг}\rangle)$$ и $$Посл(\langle{множ}\rangle)$$ для
примера 3 (см. пункт 15.1.);
Ответ. (а) Посл(E) = {[, (}. (б) Посл(E) = {[, (} ; Посл(T) пусто (никакое T -слово не является началом другого). (в) $$Посл(\langle{слаг}\rangle) = \{\hbox{\texttt{*}}\}$$ ; $$Посл(\langle{множ}\rangle)$$ пусто.
Кроме того, для каждого Нач(N ) множество всех терминалов, являющихся первыми буквами непустых $$N$$ -слов. Это обозначение - вместе с предыдущим - позволит дать достаточное условие
корректности процедуры ReadK в описанной выше ситуации.
15.2.3.
Доказать, что если Посл(L) не пересекается с Нач(M) и множество всех M -слов непусто, то ReadK корректна.
Решение. Рассмотрим два случая.
(1) Пусть после ReadL b ложно.
В этом случае ReadL читает со входа максимальное L -начало $$A$$, не являющееся L -словом. Оно является K -началом (здесь важно, что множество M -слов
непусто.). Будет ли оно максимальным K -началом среди
начал входа? Если нет, то $$A$$ является началом
слова $$BC$$, где $$B$$ есть L -слово, $$C$$ есть M -начало и $$BC$$ -
более длинное начало входа, чем $$A$$. Если $$B$$ длиннее $$A$$,
то $$A$$ - не максимальное начало входа, являющееся L -началом, что противоречит корректности ReadL.
Если $$B = A$$, то $$A$$ было бы L -словом, а это не так. Значит, $$B$$ короче $$A$$, $$C$$ непусто и первый символ слова $$C$$ следует в $$A$$ за последним символом
слова $$B$$, т.е. Посл(L) пересекается с Нач(M). Противоречие. Итак, $$A$$ максимально. Из сказанного следует также, что $$A$$ не является K -словом. Корректность процедуры ReadK в этом случае проверена.
(2) Пусть после ReadL b
ReadK начало
входа имеет вид $$AB$$, где $$A$$ есть L -слово, а $$B$$ есть M -начало. Тем самым $$AB$$ есть K -начало. Проверим
его максимальность. Пусть $$C$$ есть большее K -начало.
Тогда либо $$C$$ есть L -начало (что невозможно, так как $$A$$ было максимальным L -началом), либо $$C =
A'B'$$, где $$A'$$ - L -слово, $$B'$$ - M -начало. Если $$A'$$ короче $$A$$, то $$B'$$ непусто и начинается с символа, принадлежащего и Нач(M), и Посл(L), что невозможно.
Если $$A'$$ длиннее $$A$$, то $$A$$ - не максимальное L -начало.
Итак, $$A' = A$$. Но в этом случае $$B'$$ есть
продолжение $$B$$,
что противоречит корректности ReadM. Итак, $$AB$$ -
максимальное K -начало. Остается проверить правильность
выдаваемого процедурой ReadK значения переменной b.
Если оно M -слово, и надо проверить, что $$AB$$ - не K -слово. В самом деле, если бы выполнялось $$AB = A'B'$$, где $$A'$$ - L -слово, $$B'$$ - M -слово, то $$A'$$ не
может быть длиннее $$A$$ ( ReadL читает максимальное
слово), $$A'$$ не может быть равно $$A$$ (тогда $$B'$$ равно $$B$$
и не является M -словом) и $$A'$$ не может быть
короче $$A$$ (тогда первый символ $$B'$$ принадлежит и Нач(M), и Посл(L) ). Задача решена.
Перейдем теперь к другому частному случаю. Пусть
в КС-K нет.
15.2.4.
Считая, что ReadL, ReadM и ReadN корректны
(для L, M и N ) и что множества Нач(L), Нач(M) и Нач(N) не пересекаются, написать процедуру, корректную для K.
Решение. Схема процедуры такова:
procedure ReadK; begin | if (Next принадлежит Нач(L)) then begin | | ReadL; | end else if (Next принадлежит Нач(M)) then begin | | ReadM; | end else if (Next принадлежит Нач(N)) then begin | | ReadN; | end else begin | | b := true или false в зависимости от того, | | выводимо ли пустое слово из K или нет | end; end;
Докажем, что ReadK корректно реализует K. Если Next не принадлежит ни одному из множеств Нач(L), Нач(M), Нач(N),то пустое слово является наибольшим началом входа, являющимся K -началом. Если Next
принадлежит одному (и, следовательно, только одному) из этих
множеств, то максимальное начало входа, являющееся K -началом, непусто и читается соответствующей процедурой.
15.2.5. Используя сказанное, составить процедуру
распознавания выражений для
Решение. Эта
procedure ReadK; begin | ReadL; | if b then begin | | ReadM; | end; | if b then begin | | ReadN; | end; end;
Для ее корректности надо, чтобы Посл(L) не пересекалось с Нач(MN) (которое равно Нач(M), если из M не выводится пустое слово, и равно Нач(M) и Нач(N), если выводится), а также чтобы Посл(M) не пересекалось с Нач(N).
Аналогичным образом правила$$\begin{align*} \hbox{\texttt{K}}\to\hbox{\texttt{L M N}}\\ \hbox{\texttt{K}}\to\hbox{\texttt{P Q}}\\ \hbox{\texttt{K}}\to \end{align*}$$ приводят к процедуре
procedure ReadK; begin | if (Next принадлежит Нач(LMN)) then begin | | ReadL; | | if b then begin ReadM; end; | | if b then begin ReadN; end; | end else if (Next принадлежит Нач(PQ)) then begin | | ReadP; | | if b then begin ReadQ; end; | end else begin | | b := true; | end; end;
корректность которой требует, чтобы Нач(LMN) не
пересекалось с Нач(.
Читая приведенную далее программу, полезно иметь в виду соответствие между русскими и английскими словами:$$\begin{tabular}{ll} ВЫРажение EXPRession \\ ОСТаток ВЫРажения REST of EXPRession\\ СЛАГаемое ADDitive term\\ ОСТаток СЛАГаемого REST of ADDitive term\\ МНОЖитель FACTor \end{tabular}$$
procedure ReadSymb (c: Symbol);
| b := (Next = c);
| if b then begin
| | Move;
| end;
end;
procedure ReadExpr;
| ReadAdd;
| if b then begin ReadRestExpr; end;
end;
procedure ReadRestExpr;
| if Next = '+' then begin
| | ReadSymb ('+');
| | if b then begin ReadExpr; end;
| end else begin
| | b := true;
| end;
end;
procedure ReadAdd;
| ReadFact;
| if b then begin ReadRestAdd; end;
end;
procedure ReadRestAdd;
| if Next = '*' then begin
| | ReadSymb ('*');
| | if b then begin ReadAdd; end;
| end else begin
| | b := true;
| end;
end;
procedure ReadFact;
| if Next = 'x' then begin
| | ReadSymb ('x');
| end else if Next = '(' then begin
| | ReadSymb ('(');
| | if b then begin ReadExpr; end;
| | if b then begin ReadSymb (')'); end;
| end else begin
| | b := false;
| end;
end;
Осталось обсудить проблемы, связанные с взаимной рекурсивностью этих процедур (одна использует другую и наоборот). В K ничего не
выводится, Посл(K) и Нач(K) пусты, но написанная по нашим канонам процедура
procedure ReadK; begin | ReadK; | if b then begin | | ReadK; | end; end;
не заканчивает работы.)
В данном случае процедуры ReadRestExpr, ReadRestAdd, ReadFact либо завершаются, либо
уменьшают длину непрочитанной части входа. Поскольку любой цикл
вызовов включает одну из них, то зацикливание невозможно.
15.2.6.
Пусть в K
в левой части, имеющих вид$$\begin{align*}
\hbox{\texttt{K}}\to\hbox{\texttt{L K}}\\
\hbox{\texttt{K}}\to
\end{align*}$$
по которым K -слово представляет собой конечную
последовательность L -слов, причем множества Посл(L) и Нач(K) (в данном случае равное Нач(L) ) не пересекаются. Используя корректную для L процедуру ReadL, написать корректную для K процедуру ReadK, не используя
Решение. По нашим правилам следовало бы написать
procedure ReadK; begin | if (Next принадлежит Нач(L)) then begin | | ReadL; | | if b then begin ReadK; end; | end else begin | | b := true; | end; end;
Эта рекурсивная процедура
procedure ReadK; begin | b := true; | while b and (Next принадлежит Нач(L)) do begin | | ReadL; | end; end;
Формально можно проверить эту эквивалентность так. Завершаемость в обоих случаях ясна. Достаточно проверить поэтому, что тело рекурсивной процедуры
if (Next принадлежит Нач(L)) then begin | ReadL; | if b then begin | | b := true; | | while b and (Next принадлежит Нач(L)) do begin | | | ReadL; | | end; | end; end else begin | b := true; end;
Первую команду b:= можно выкинуть (в этом месте и так b
b := true; if (Next принадлежит Нач(L) then begin | ReadL; | if b then begin | | while b and (Next принадлежит Нач(L)) do begin | | | ReadL; | | end; | end; end;
Теперь внутренний if можно выкинуть (если b ложно,
цикл while все равно не выполняется) и добавить в условие
внешнего if условие b (которое все равно
b := true; if b and (Next принадлежит Нач(L)) then begin | ReadL; | while b and (Next принадлежит Нач(L)) do begin | | ReadL; | end; end;
что
15.2.7. Доказать корректность приведенной выше нерекурсивной программы непосредственно, без ссылок на рекурсивную.
Решение. Рассмотрим наибольшее начало входа,
являющееся K -началом. Оно представляется в виде
L -слов и, возможно, одного непустого L -начала, не являющегося L -словом. b $$\Leftrightarrow$$
(последнее прочитанное является L -словом).
Сохранение L -словом
(из числа оставшихся) идет символ из Нач(L), и потому это слово - максимальное начало входа, являющееся L -началом.
На практике при записи K имеют вид$$\begin{align*}
\hbox{\texttt{K}}\to \hbox{\texttt{L K}}\\
\hbox{\texttt{K}}\to
\end{align*}$$
(т.е. K -слова - это последовательности L -слов),
то этих правил не пишут, а вместо K пишут L в
Например, рассмотренная выше
15.2.8.
Написать процедуру, корректную для $$\langle{выр}\rangle$$, следуя этой
Решение.
procedure ReadSymb (c: Symbol);
| b := (Next = c);
| if b then begin Move; end;
end;
procedure ReadExpr;
begin
| ReadAdd;
| while b and (Next = '+') do begin
| | Move; ReadAdd;
| end;
end;
procedure ReadAdd;
begin
| ReadFact;
| while b and (Next = '*') do begin
| | Move; ReadFact;
| end;
end;
procedure ReadFact;
begin
| if Next = 'x' do begin
| | Move; b := true;
| end else if Next = '(' then begin
| | Move; ReadExpr;
| | if b then begin ReadSymb (')'); end;
| end else begin
| | b := false;
| end;
end;
15.2.9.
В последней процедуре команду b:= можно опустить.
Почему?
Решение. Можно предполагать, что все процедуры вызываются при b=.
В этом разделе мы рассмотрим еще один метод проверки
выводимости в КС-
Определение.
15.3.1. Для каждого выводимого слова (из терминалов) существует его левый вывод.
Решение. Различные нетерминалы заменяются независимо; если в процессе вывода появилось слово $$\ldots K\ldots L\ldots$$, где $$K$$, $$L$$ - нетерминалы, то замены $$K$$ и $$L$$
можно производить в любом порядке. Поэтому можно
перестроить вывод так, чтобы стоящий левее
15.3.2. В
Решение. На первом шаге можно применить только правило (2):$${\texttt{E}} \to {\texttt{TE}}$$
Что будет дальше с T? Так как слово $$A$$ начинается на [, то может примениться только правило (4):$${\texttt{E}} \to {\texttt{TE}}\to {\texttt{[E]E}}$$
Первое E должно замениться на (иначе вторым символом была бы скобка ] ):$${\texttt{E}} \to {\texttt{TE}}\to{\texttt{[E]E}} \to{\texttt{[TE]E}}$$
и T должно заменяться по (3):$${\texttt{E}}\to{\texttt{TE}}\to{\texttt{[E]E}} \to{\texttt{[TE]E}}\to{\texttt{[(E)E]E}}$$
Далее первое E должно замениться на пустое слово (иначе
третьей буквой слова будет ( или [ - только на
эти символы может начинаться слово, выводимое из T ):$${\texttt{E}}\to{\texttt{TE}}\to{\texttt{[E]E}}\to{\texttt{[TE]E}}\to{\texttt{[(E)E]E}}\to{\texttt{[()E]E}}$$
и далее$$\begin{multiline*}
\ldots\to \hbox{\texttt{[()TE]E}}
\to\hbox{\texttt{[()(E)E]E}}
\to\hbox{\texttt{[()(TE)E]E}}
\to\hbox{\texttt{[()([E]E)E]E}}\to{}\\
%
\to\hbox{\texttt{[()([\,]E)E]E}}
\to\hbox{\texttt{[()([\,])E]E}}
\to\hbox{\texttt{[()([\,])]E}}
\to\hbox{\texttt{[()([\,])]}}
\end{multiline*}$$
Что требуется от
Рассмотрим множество Нач(LMN)
тех терминалов, с которых начинаются непустые слова,
выводимые из $$LMN$$. (Это множество равно Нач(L), Нач(M), если из $$L$$ выводится пустое слово, а также с Нач(N), если из $$L$$ и из $$M$$ выводится пустое слово.) Чтобы описанный метод был применим, надо,
чтобы Нач(LMN), Нач( и Нач(R) не пересекались. Но этого мало. Ведь может быть так, например, что из $$LMN$$ будет выведено пустое слово, а из слова $$U$$ будет выведено слово, начинающееся на букву из Нач(. Следующие определения учитывают эту проблему.
Напомним, что определение
Для каждого слова $$X$$ из терминалов и нетерминалов
через Нач(X) обозначаем множество всех терминалов,
с которых начинаются непустые слова из терминалов,
выводимые из $$X$$. (В случае, если из любого Нач(X) слова только из терминалов или любые слова. Мы будем предполагать далее,
что это условие выполнено.)
Для каждого Послед(K) обозначим множество терминалов, которые встречаются в выводимых (в Посл(K) предыдущего раздела!) Кроме того, в Послед(K) включается символ , если существует выводимое слово, оканчивающееся на $$K$$.
Для каждого правила$$K \to V$$
(где $$K$$ -
Определение.
15.3.3.
Является ли
Решение. Нет: символ # принадлежит множествам направляющих символов для обоих правил (для второго - поскольку # принадлежит $$Послед(K)$$ ).
15.3.4.
Написать
Решение.$$\begin{align*} \hbox{\texttt{K}}\to\hbox{\texttt{\# K}}\\ \hbox{\texttt{K}}\to \end{align*}$$
Как говорят, "леворекурсивное" правило заменено на "праворекурсивное".
Следующая задача показывает, что для
15.3.5.
Пусть дано выводимое в .)
Решение. Эти буквы принадлежат направляющим множествам различных правил.
15.3.6.
Доказать, что если слово выводимо в
Решение. Предыдущая задача показывает, что на каждом шаге левый вывод продолжается однозначно.
15.3.7.
Решение. Пусть из $$K$$ выводится $$KU$$, где $$K$$ -
Таким образом, к
15.3.8.
Используя сказанное, построить алгоритм проверки выводимости слова из терминалов в
Решение. Мы следуем описанному выше методу поиска левого вывода, храня лишь часть слова, находящуюся правее уже
прочитанной части входного слова. Другими словами, мы храним
слово S из терминалов и нетерминалов, обладающее такими свойствами (прочитанную часть входа обозначаем через A ):
AS выводимо в ASЭти свойства вместе будем обозначать "(И)".
Вначале A пусто, а S состоит из единственного символа - начального
Если в некоторый момент S начинается на
терминал t и $${\texttt{t}} = {\texttt{Next}}$$, то
можно выполнить команду и удалить символ t,
являющийся начальным в S, поскольку при этом AS не
меняется.
Если S начинается на терминал t и $${\texttt{t}}\neq{\texttt{Next}}$$, то входное слово
невыводимо - ибо по условию любой его вывод должен проходить через AS. (Это же справедливо и в случае $${\texttt{Next}} =
{\texttt{EOI}}$$.)
Если S пусто, то из условия (И) следует, что входное слово
выводимо тогда и только тогда, когда $${\texttt{Next}} =
{\texttt{EOI}}$$.
Остается случай, когда S начинается с некоторого
K. По доказанному выше все левые выводы
из S слов, начинающихся на символ Next, начинаются
с применения к S одного и того же правила - того, для
которого Next принадлежит направляющему множеству. Если
таких правил нет, то входное слово невыводимо. Если такое
правило есть, то нужно применить его к первому символу
слова S - при этом свойство (И) не нарушится. Приходим
к такому алгоритму:
s := пустое слово;
error := false;
{error => входное слово невыводимо;}
{not error => (И)}
while (not error) and not ((Next=EOI) and (S пусто))
| | do begin
| if (S начинается на терминал, равный Next) then begin
| | Move; удалить из S первый символ;
| end else if (S начинается на терминал, не равный Next)
| | then begin
| | error := true;
| end else if (S пусто) and (Next <> EOI) then begin
| | error := true;
| end else if (S начинается на нетерминал и Next входит в
| | направляющее множество одного из правил для этого
| | нетерминала) then begin
| | применить это правило
| end else if (S начинается на нетерминал и Next не входит
| | в направляющее множество ни одного из правил для этого
| | нетерминала) then begin
| | error := true;
| end else begin
| | {так не бывает}
| end;
end;
{входное слово выводимо <=> not error}
Алгоритм заканчивает работу, поскольку при появлении
терминала в начале слова S происходит чтение со входа
или остановка, а бесконечный цикл сменяющих друг друга
нетерминалов в начале S означал бы, что
Замечания.
S как Next.
После этого на каждом шаге цикла будет читаться S, и небольшую программу,
выполняющую действия в соответствии с этой таблицей.15.3.9.
При проверке того, относится ли данная
Решение.Пусть, например, в Нач(T) и Послед(T).
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.