Порождающие грамматики Хомского
Время на прочтение
12 мин
Количество просмотров 114K
Небольшое предисловие
Этот текст является продолжением поста , в котором автор попытался как можно более просто и без сложных математических выкладок описать понятия формального языка и грамматики. На этот текст пришло достаточно много откликов и автор счел себя обязанным написать продолжение.
Ниже описывается формализм порождающих грамматик Хомского. Методы задания языка с помощью порождающих грамматик сейчас довольно популярны, особенно для машинной обработки компьютерных языков. Но обычно изучение порождающих грамматик в теории трансляторов заканчивается на контекстно-свободных грамматиках. Последние являются довольно узким специальным классом порождающих грамматик Хомского и обычно используются как вид категориальных грамматик (как конкретно это делается, будет показано ниже) для задания синтаксических анализаторов. Последнее обстоятельство только затуманивает понимание подхода Хомского. Дальнейшее изложение предназначено тем, кому интересно понять, в чем состоит этот подход.
Определение порождающей грамматики
Грамматика представляет собой конечное описание формального языка. Формальный язык, в свою очередь, является произвольным множеством цепочек, составленных из символов некоторого конечного алфавита. Произвольность множества здесь понимается в том смысле, что оно может быть бесконечным, конечным или пустым.
Формализм порождающих грамматик Хомского был введен Ноамом Хомским в конце 50-х годов прошлого века. За короткое время этот формализм обрел необычайную популярность. Некоторое время порождающие грамматики рассматривались как панацея — универсальный подход для задания всевозможных языков, в том числе и естественных (т.е. языков, которые люди используют для повседневного общения между собой). Но время показало, что порождающие грамматики для описания естественных языков не очень удобны. Сейчас порождающие грамматики применяются, в основном, для описания синтаксиса формальных языков, подобных языкам программирования и другим компьютерным языкам.
Порождающая грамматика Хомского задается как множество «правил порождения» (продукций). Каждое правило является просто парой цепочек (w', w'')
и задает возможность замены левой цепочки на правую при генерации цепочек языка, задаваемого грамматикой. По этой причине, правила обычно записывают в виде w' --> w''
, указывая конкретно, что на что можно заменять. Множество правил в грамматике должно быть непустым и конечным, и обычно обозначается латинской P.
Цепочки в правилах грамматики могут быть составлены из символов двух алфавитов: алфавита терминальных символов (терминалов) и алфавита нетерминальных символов (нетерминалов). Алфавит терминалов обозначают через T. Этот алфавит на самом деле совпадает с алфавитом того формального языка, который задает данная грамматика. Смысл термина «терминальный» состоит в том, что в правилах грамматики в левой части не может быть цепочек, которые составлены только из терминальных символов. Поэтому, если такая цепочка получилась в результате подстановки, то следующая процесс порождения цепочки остановится (terminate). Нетерминальные символы используются в промежуточных порождениях цепочек. Смысл нетерминала в задании алгоритма порождения цепочки может быть самый разный и обычно зависит от типа грамматики, в которой этот символ используется. Различные примеры использования нетерминальных символов будут рассмотрены ниже.
Но один нетерминальный символ всегда имеет один и тот же смысл — он обозначает все цепочки языка. Называется этот нетерминал «начальным нетерминальным символов порождающей грамматики» и обычно обозначается посредством латинского S (start или sentence). В каждой порождающей грамматике обязательно должно быть правило, к которого левая часть состоит из единственного начального нетерминала, иначе в данной грамматике нельзя будет породить даже одной цепочки.
Итак, порождающая грамматика Хомского — это четверка G = {N, T, P, S}
, где
-
N
— конечный алфавит нетерминальных символов. -
T
— конечный алфавит терминальных символов (совпадает с алфавитом языка, задаваемого грамматикой). -
P
— конечное множество правил порождения. -
S
— начальный нетерминал грамматикиG
.
Язык порождающей грамматики
Порождающая грамматика Хомского задает язык посредством конечного числа подстановок цепочек из начального нетерминала грамматики на основе правил порождения. Опишем это чуть более конкретно.
Шаг порождения w' alpha w'' => w' beta w''
состоит в замене подцепочки alpha
на подцепочку beta
в соответствии с правилом порождения alpha --> beta
. При этом, очевидно, из цепочки w' alpha w''
получается цепочка w' beta w''
. Иначе говоря, если имеется некоторая цепочка и некоторая ее подцепочка является левой частью какого-то правила грамматики, то мы имеем полное право заменить эту левую часть правила на правую. Конечная последовательность шагов порождений называется порождением. Нуль или более порождений будет обозначать знаком =>*
. Обозначение alpha =>* beta
говорит о том, что цепочка beta
получена из цепочки alpha
конечным числом подстановок на основе правил порождения. В этом обозначении может быть так, что подстановка (порождение) не была применена ни разу, в этом случае цепочка alpha
совпадает с beta
.
Итак, язык порождающей грамматики G = {N, T, P, S}
— это множество цепочек, составленных из терминальных символов и порожденных из начального символа грамматики. Математическая формула такова: L = {w | S =>* w}
.
Для иллюстрации приведем два простых примера.
Пример очень простого языка
Пусть язык L
состоит из одной цепочки, которая состоит из единственного символа a
. Иначе говоря, L = {a}
. Для порождения цепочки a
достаточно одного правила S --> a
. Единственное порождение, которое может быть в данной грамматике — это S => a
.
Следует заметить, что для этого языка можно было бы ввести еще один нетерминальный символ, скажем, символ A
, а также правила S --> A
и A --> a
. Тогда единственным порождением было бы следующее: S => A => a
. Так как алфавит нетерминалов грамматики мы выбираем произвольно, становится понятно, что даже для такого простого языка имеется бесконечное множество порождающих грамматик, задающих данный язык.
Язык простых арифметических выражений
Рассмотрим язык A = {a+a, a+a+a, a+a+a+a, ...}
. Цепочки этого языка представляют собой последовательности символов a
, разделенных символами +
. Как задать правила порождения этого языка? Заметим, что каждая цепочка языка начинается с символа a
за которым идет одна или более цепочек +a
. Соответственно, возникает мысль сначала породить символ a
, а затем каждая цепочка языка будет получаться присоединением к этому символу справа одной или более цепочек +a
. Чтобы отделить эти две стадии порождения друг от друга, введем нетерминальный символ A
. Тогда, получим грамматику со следующими правилами: S --> aA, A --> +aA, A --> +a
.
Рассмотрим, например, как можно породить цепочку a+a+a
. S => aA => a+aA => a+a+a
. В этом порождении последовательно были применены все три правила: S --> aA, A --> +aA, A --> +a
.
Язык A
содержит бесконечное число цепочек, значит, ограничения на длину цепочки в этом языке нет. Единственный способ порождать цепочки неограниченной длины, это использовать рекурсивные правила порождения, т.е. правила, в которых в правой части правила содержится его левая часть. В примере выше это правило A --> +aA
. Левая часть — это цепочка из единственного символа A
, который также содержится и в правой части. Такая рекурсия позволяет последовательно применять в подстановке одно и то же правила, увеличивая, сколько необходимо, длину порождаемой цепочки. Рекурсия может быть и опосредованной, через промежуточные правила. Например, правила A --> aBc, B --> deA
задают опосредованную рекурсию цепочки A
.
Классы грамматик
Ноам Хомский ввел классы грамматик (и соответствующие классы языков) задавая ограничения на вид правил порождающей грамматики. Каждый класс грамматик имеет свою описательную мощность. Описательную мощность класса грамматик можно охарактеризовать, как возможность выражений в правилах грамматики определенных синтаксических отношений. Рассмотрим, как классы грамматик задают синтаксические отношения.
Грамматики типа 3
Этот класс грамматик задает алгоритм порождения цепочек присоединением некоторого количества терминальных символов с правого или левого края порождаемой цепочки. Очевидно, что правила для такого метода порождения должны иметь вид A --> alpha B
или A --> B alpha
, где alpha
— цепочка, состоящая из терминальных символов. В этом случае, если имеется промежуточная (в процессе порождения) цепочка X1..Xn A
, то замена в соответствии с правилом A --> alpha B
даст цепочку X1..Xn alpha B
. Например, для правил S --> aaaA
, A --> abcA
и A --> bbb
можно задать порождение S => aaaA => aaaabcA => aaaabcbbb
.
Синтаксическое отношение, которое задается грамматиками типа 3, можно обозначить термином «быть рядом». Под «рядом» здесь подразумевается как непосредственно рядом, если это задано в правой части какого-то правила порождения, так и опосредованно рядом, через нетерминальные символы в связанных между собой правилах порождения.
Для математической строгости строку терминальных символов в правилах грамматик типа 3 разбивают на несколько правил с одним терминальным символом в правой части. Например, если имеется правило A --> abcB
, то его можно заменить на следующие правила, применение которых в результате порождает ту же цепочку: A --> a A1
, A1 --> b A2
, A2 --> cB
. Иначе говоря, подстановка A => abcB
эквивалентна последовательности подстановок A => a A1 => a b A2 => abcB
. Такие грамматики, где нетерминальный символ стоит справа в правой части правила, называют праволинейными грамматиками, если в правой части нетерминальный символ стоит слева от терминала, то грамматику называют леволинейной.
Зададим, например леволинейную грамматику для языка A = {a+a, a+a+a, a+a+a+a, ...}
. Правила грамматики типа 3, как было рассмотрено выше, это: S --> aA, A --> +aA, A --> +a
. Здесь цепочки порождаются присоединением пары символов справа. Изменим грамматику так, чтобы символы присоединялись слева, а также добавим нетерминальные символы, чтобы каждый раз добавлять только по одному символу. Получим грамматику:
S --> Aa
A --> B+
B --> Aa
B --> a
Вот как выглядит порождение цепочки a+a+a
: S => Aa => B+a => Aa+a => B+a+a => a+a+a
.
Внимательный читатель вероятно заметил, что грамматика типа 3 похожа на попрождающий автомат, в котором роль состояний играют нетерминальный символы грамматики. Одна из возможных интерпретаций этой грамматики — это, действительно, конечный автомат.
Контекстно-свободные грамматики
Контекстно-свободные грамматики имеют правила вида: A --> alpha
. В левой части правила должен стоять один символ (конечно, нетерминальный), а справа может быть любая цепочка из терминальных и нетерминальных символов (в том числе и пустая).
КС-грамматики задают два вида синтаксических отношений: отношение «быть рядом» и отношение «быть частью» или отношение иерархии. Отношение иерархии наиболее естественно для человеческого ума. Человеку свойственно типизировать вещи, т.е. рассматривать конкретные объекты своего мышления как части какого-то общего типа (класса). Каждая вещь, о которой думает человек, является экземпляром некоторого класса. Например, конкретный стул является экземпляром класса «стул» с соответствующими признаками. Человеческому уму также свойственно разделять типы на подтипы, двигаясь от более конкретных типов к более абстрактным. Скажем, стул есть подтип типа мебель, мебель есть подтип типа предмет, предмет есть подтип типа объект и т.п. Отношение «тип-подтип» и есть отношение иерархии.
КС-грамматика может быть проинтерпретирована как категориальная грамматика, т.е. грамматика типов. Символы грамматики в этом случае могут мыслиться как типы, а правила тогда задают отношение иерархии между типами. Нетерминальные символы выступают как сложные типы, а терминальный символы — как атомарные типы, у которых не может быть подтипов. Такая интерпретация КС-грамматики очень популярна и часто используется при создании трансляторов языков. Но, задавая класс КС-грамматик, Хомский имел ввиду нечто другое.
Ввиду того, что КС-грамматика является порождающей, она задает алгоритм (строго говоря, не алгоритм, но исчисление — многовариантный алгоритм) порождения цепочек языка. Порождение здесь задается не только присоединением цепочек справа или слева имеющейся цепочки, но и вставкой цепочки куда-нибудь внутрь имеющейся. Вставка производится заменой нетерминального символа в цепочке на цепочку, которая стоит в правой части некоторого правила, в левой части которого находится этот нетерминал. Скажем, цепочку aabBBACbbb
можно преобразовать в цепочку aabBBaaaCbbb
, если есть правило A --> aaa
. В этом смысле, порождаемая цепочка растет не равномерно с какого-то края, но как-бы «пухнет» изнутри.
Проиллюстрируем сказанное на примере. Рассмотрим язык L = {a^n b^n | n = 1, 2, 3,...}
. Выражение a^n
здесь означает повторение n
раз символа a
. Таким образом, язык L
состоит из цепочке вида ab, aabb, aaabbb
и т.д. Зададим КС-грамматику для этого языка. Для этого заметим, что из цепочки языка можно получить другую цепочку языка, присоединяя к первой слева символ a
, а справа символ b
. Скажем, если имеется цепочка aabb
, то из нее можно получить цепочку aaabbb
. Это замечание дает правило порождения S --> aSb
(напомним, что цепочки языка порождаются из начального нетерминала грамматики и, значит, могут быть обозначены этим символом). Есть еще специальный случай цепочки, которая не дробится на более мелкие — это цепочка ab
. Введем для ее порождения правило S --> ab
. Итак, грамматика языка имеет правила: S --> aSb
и S --> ab
. Зададим порождение цепочки aaabbb
: S => aSb => aaSbb => aaabbb
.
Контекстно-зависимые грамматики и грамматики без ограничений
В правилах КС-грамматики нетерминальный символ в левой части правила порождения можно менять на правую часть в любом месте порождаемой цепочки, где бы этот символ не встретился. Но иногда хотелось бы различать контексты, в которых находится символ в цепочке, и в одних случаях производить замену символа, в других — нет. Правила КС-грамматики этого делать не позволяют, поэтому для таких случаев необходимы правила специального вида.
Контекстно-зависимая грамматика имеет правила вида w' A w'' --> w' alpha w''
. Здесь w'
и w''
— цепочки (может быть пустые), составленные из терминальных и нетерминальных символов грамматики, alpha
— непустая цепочка из тех же символов. Иначе говоря, нетерминальный символ A
заменяется на цепочку alpha
в контексте цепочек w'
и w''
.
С КЗ-грамматикой связан другой класс грамматик — неукорачивающие грамматики. Правила в таких грамматиках должны удовлетворять одному условию: длина правой части должна быть не меньше длины левой части. Так как в правилах КЗ-грамматик имеется условие, чтобы цепочка alpha
была непустая, то эти грамматики также являются неукорачивающими. Но, самое интересное состоит в том, что для каждого языка, заданного неукорачивающей грамматикой, может быть придумана КЗ-грамматика, задающая тот же язык. Иначе говоря, классы языков, задаваемых КЗ-грамматиками и неукорачивающими грамматиками, совпадают.
Зачем так необходимо выделять класс языков, задаваемых неукорачивающими грамматиками? Дело в том, что для таких языков можно задать распознающий автомат. Распознающая грамматика конструируется следующим образом: получая на вход цепочку, последовательно делаем порождения, упорядочивая их по длине порождаемой цепочки. Т.к. грамматика неукорачивающая, то таких порождений будет конечное множество и, если среди них не нашлось совпадения с данной на вход цепочкой, то напечатать «нет».
Для грамматики без ограничений на вид правил такой алгоритм распознавания в общем случае построить нельзя. Порождаемая цепочка может вести себя как «гармошка», раздуваясь и сдуваясь в процессе порождения. Поэтому достижение порождаемой цепочкой определенной длины не гарантирует, что далее в процессе порождения не будет получена поданная на вход цепочка.
Действительно ли КЗ-языки образуют собственный класс, не совпадает ли этот класс с классом КС-языков. Иначе говоря, есть ли язык, для которого гарантировано нельзя задать КС-грамматику, но можно задать КЗ-грамматику? Ответ: да, такие языки есть. В качестве примера такого языка можно привести следующий язык L = {ww}
. Цепочки этого языка составлены из двух повторяющихся цепочек над каким-то алфавитом. Доказывать, что для этого языка нельзя построить КС-грамматики мы здесь не будем. КЗ-грамматику можно задать на основе следующего соображения. Сначала сгенерировать цепочку w
и нетерминальный символ, скажем A
, т.е. получить цепочку Aw
. Затем продвинуть символ A
сквозь цепочку w
, генерируя по ходу копии символов этой цепочки, после чего продвинуть эти символы направо. Примерно то же, как это будет реализовано в примере ниже.
Рассмотрим пример задания грамматики для языка L = {a^n^2 | n = 1, 2, 3, ...}
. Цепочки этого языка состоят из символов a
, причем число этих символов есть квадрат натурального числа: 1, 4, 9, 25 и т.д. Мы зададим для этого языка грамматику без ограничений. Генерация цепочек будет состоять из следующих этапов:
- Генерация
n
символов для некоторого натурального числаn
. - Порождение из этого числа символов
n^2
символов. - Преобразование этих символов в символы
a
.
Для реализации первого этапа добавляем правила
S --> LS'R
S' --> AS'B
S' --> AB
Первым правилом оборачиваем порождаемую цепочку в ограничители L
и R
. Они нам понадобятся в дальнейшем для реализации третьей фазы генерации. Оставшиеся два правила просто генерируют цепочку вида AA...ABB...B
, где число символов A
и B
совпадает.
Теперь необходимо породить n^2
символов на основе цепочке AA...ABB...B
. Это делает простым приемом. Двигаем символы B
налево и, при каждом переходе через символ A
, порождать еще один символ C
. Через символы C символ A
может свободно проходить направо, а символ B
— налево. Правила для этого этапа следующие:
AB --> BAC
AC --> CA
CB --> BC
Когда все символы B
дойдут до левого края, перейдя символы A
, символов C
будет ровно n^2
.
Теперь надо освободиться от символов L
, A
, B
и R
, а также преобразовать символы C
в символы a
. Для этого аннигилируем символ B
при его проходе до левого края, т.е. до символа L
. Соответственно поступаем и с символом A
на правом крае. При реализации такой стратегии останется цепочка вида LCC....CR
. Чтобы избавится от символов L
и R
, начинаем двигать левый ограничитель к правому и, при их соприкосновении, уничтожаем эти символы. Заодно, при прохождении через символы L
, преобразуем их в символы a
. Приведем правила для этой фазы генерации.
LB --> L
AR --> R
LC --> aL
LR --> epsilon
Здесь epsilon
обозначает пустую цепочку.
Приведем в качестве примера порождение цепочки aaaa
: S => LS'R => LAS'BR => LAABBR => LABACBR => LBACACBR => LACACBR => LACABCR => LACBACCR => LABCACCR => LBACCACCR => LACCACCR => LCACACCR => LCCAACCR => LCCACACR => LCCACCAR => LCCACCR => LCCCACR => LCCCCAR => LCCCCR => aLCCCR => aaLCCR => aaaLCR => aaaaLR => aaaa
Заключение
Автор надеется, что последний пример ясно продемонстрировал читателю, что порождающая грамматика Хомского представляет собой своего рода программу, предназначенную для генерации цепочек формального языка, задаваемого этой грамматикой. Язык задания программы довольно специфический, соответственно и реализация «программ генерации» (грамматик) требует опыта и определенной привычки в их написании.
Порождающие грамматики Хомского основаны на глубоких идеях и нет ничего удивительного в том, что какие-то подклассы этого вида грамматик генерируют не только определенный вида языков, но и пересекаются с идеями из других разделов математической лингвистики. К таких разделам относятся категориальные грамматики и распознающие автоматы. Конечно, в этом тексте описаны только основные идеи, теория порождающих грамматик шире и глубже, чтобы ее можно было описать в рамках одной статьи.
Порождающие грамматики (грамматики Хомского)
Как уже отмечалось, классическая теория формальных языков изучает прежде всего синтаксис языка. Она вводит математическую модель синтаксиса, которая описывает механизмы порождения и распознавания “правильно построенных” цепочек. В этом разделе мы рассмотрим первый из этих механизмов. Задать такой механизм — значит описать некую процедуру, позволяющую вывести (или, как обычно говорят, породить) произвольную цепочку языка согласно определенному конечному множеству правил. Последнее очень важно: язык может быть бесконечным, но множество правил, с помощью которых выводятся его цепочки, обязано быть конечным. Заметим, что не каждый язык может быть представлен таким образом, но мы в рамках этого учебника будем рассматривать только такие языки, т.е. языки, синтаксис которых задается конечным множеством правил. Эти языки, вообще говоря бесконечные, но допускающие конечное описание, называют перечислимыми. Таким образом, мы рассматриваем только перечислимые языки.
Классическим способом определения языков через порождающую процедуру является определение их с помощью порождающих грамматик, или грамматик Хомского.
Порождающая грамматика позволяет выводить (порождать) цепочки языка из некоторой начальной цепочки с помощью определенных правил замены (или правил подстановки, правил переписывания). Порождение есть пошаговый процесс, в котором на каждом шаге из цепочки, уже полученной на предыдущем шаге (в частности, из начальной), можно путем применения к ней правил замены получить новую цепочку. Именно так мы действуем, решая какую-нибудь математическую задачу, например выполняя дифференцирование и переходя от одной формулы к другой с помощью таблицы производных.
Порождающая грамматика (далее просто грамматика) задается упорядоченной четверкой , в которой — алфавит, называемый терминальным алфавитом; — алфавит, называемый нетерминальным алфавитом, причем пересечение и пусто; — выделенный символ нетерминального алфавита, называемый аксиомой; — конечное множество правил вывода, или продукций. Каждое правило вывода является упорядоченной парой цепочек в алфавите , причем цепочка должна содержать вхождение хотя бы одного символа нетерминального алфавита; цепочку называют левой, цепочку — правой частью правила вывода.
Правило вывода принято записывать в таком виде: , разделяя левую и правую части “стрелкой”, которая рассматривается как “метасимвол” и не принадлежит ни одному из алфавитов грамматики. Буквы терминального алфавита грамматики называют терминальными символами (или просто терминалами); буквы нетерминального алфавита называют нетерминальными символами (или нетерминалами). Любую цепочку в терминальном (нетерминальном) алфавите называют терминальной (нетерминальной) цепочкой. Алфавит называют объединенным алфавитом грамматики .
Пример 7.3. Четверка
задает грамматику с терминальным алфавитом , с нетерминальным алфавитом , с аксиомой и множеством правил вывода , элементы которого перечислены в фигурных скобках после аксиомы. Обычно ради наглядности правила вывода грамматики выписывают отдельно:
Замечание 7.1. Нам будет удобно условиться о некоторых обозначениях, которыми мы все время будем пользоваться, имея дело с грамматиками. Терминалы будем обозначать малыми латинскими буквами из начала латинского алфавита: и т.д.; нетерминалы — большими латинскими буквами из начала и середины латинского алфавита: и т.д.; терминальные цепочки (т.е. слова в терминальном алфавите) — малыми латинскими буквами из середины и конца латинского алфавита: и т.д.; цепочки в объединенном алфавите — малыми греческими буквами. Наконец, большими латинскими буквами из конца алфавита будем обозначать символы объединенного алфавита или пустую цепочку.
Определение грамматики указывает пока только на то, что следует фиксировать, чтобы задать какую-либо грамматику. А именно следует фиксировать два непересекающихся алфавита, выделив в одном из них символ, названный аксиомой, а также конечное множество правил вывода. Но мы еще не знаем, каким образом “работает” грамматика как инструмент порождения (и преобразования) слов. Чтобы это понять, нужно определить важнейшее понятие выводимости в данной грамматике.
Неформально каждое правило вывода грамматики трактуется как “правило замены”, разрешающее произвольное вхождение левой части правила в некоторую цепочку в объединенном алфавите заменить его правой частью, получив (или выведя) тем самым новую цепочку. Так, для примера 7.3 мы можем от цепочки , используя правило , перейти к цепочке . В этой цепочке есть вхождение левой части правил и , а также левой части правила . Можно использовать любое из них (но какое-нибудь одно!): если мы заменим (в данном случае единственное) вхождение цепочки правой частью правила , то получим цепочку и т.д. Таким образом мы и строим так называемые выводы — последовательности цепочек, в которых каждый последующий член получается из предыдущего заменой, подобной только что рассмотренной.
Определение 7.6. Цепочка непосредственно выводима в грамматике из цепочки (или из непосредственно выводится ), если существуют такие цепочки и и такое правило вывода из , что (т.е. существует вхождение левой части правила вывода в цепочку 7), a .
Бинарное отношение на множестве цепочек в объединенном алфавите, которое состоит из всех упорядоченных пар , таких, что вторая цепочка непосредственно выводится из первой, называют отношением непосредственной выводимости. Его будем обозначать символом , опуская зачастую имя грамматики: . В этом случае говорят также, что правило применяется к цепочке (и что цепочка получена применением правила к цепочке или, что равносильно, в результате замены данного вхождения левой части правила его правой частью).
В общем случае, если левая часть правила вывода входит в данную цепочку, говорят, что правило применимо к этой цепочке (или имеет место применимость правила вывода к цепочке). Если же левая часть правила вывода не входит в цепочку, то говорят, что правило не применимо к данной цепочке.
Рефлексивно-транзитивное замыкание отношения непосредственной выводимости обозначаем (опять-таки опуская имя грамматики , если это не приводит к недоразумению: ) и называем отношением выводимости. Иначе говоря, выводимость цепочки из цепочки , в грамматике имеет место, по определению, тогда и только тогда, когда найдутся такие слова , где , что
Для данной цепочки в объединенном алфавите, если к ней применимо хотя бы одно правило вывода рассматриваемой грамматики, существует в общем случае не одна, а много цепочек, непосредственно выводимых из нее. Эта неоднозначность связана с двумя факторами. Во-первых, может существовать несколько разных правил вывода, применимых к цепочке . Так, для грамматики примера 7.3 к цепочке применимы все правила грамматики, кроме первого. Тогда любая цепочка, полученная применением к указанной цепочке любого из этих правил, будет непосредственно выводима из нее. Во-вторых, если уже фиксировано правило вывода, применимое к цепочке , то существуют, вообще говоря, несколько различных вхождений левой части этого правила в цепочку . Тогда любая цепочка, полученная заменой любого из этих вхождений правой частью выбранного правила вывода, будет непосредственно выводима из . Для грамматики примера 7.3, правила и цепочки получим: и .
Разные вхождения левой части некоторого правила в заданную цепочку могут и пересекаться. Так, правило грамматики примера 7.3 можно к написанному выше слову применить двояко: и .
Заметим, наконец, что множество цепочек, непосредственно выводимых из данной, пусто тогда и только тогда, когда среди правил вывода грамматики нет ни одного, применимого к данной цепочке. Для примера 7.3 в качестве такой цепочки можно взять хотя бы цепочку .
Определение 7.7. Выводом в грамматике называют произвольную, конечную или бесконечную, последовательность слов , в которой для любого справедливо , если цепочка существует. Число называют длиной вывода, если указанная последовательность конечна и — ее последний элемент. В этом случае говорят также, что выводится из за шагов. Для положительного пишем или, если нужно специально оговорить длину вывода .
Из определений следует, что выводимость равносильна тому, что существует вывод
, где .
Мы будем использовать также термин “фрагмент вывода”, понимая под этим такую подпоследовательность вывода, которая сама является выводом. В частности, фрагмент вывода, состоящий из двух соседних его членов, называется шагом вывода.
Построение вывода в порождающей грамматике можно трактовать как “игру”, правила которой заданы множеством . Подобно тому как в игре (например, в шахматах) можно из заданной позиции перейти в одну из множества следующих позиций, используя правила игры, так и при проведении вывода в грамматике можно из данной цепочки получить одну из множества непосредственно выводимых из нее, заменяя в ней произвольное вхождение левой части правила его правой частью. При этом мы можем выбрать любое правило, применимое к очередной цепочке, и среди различных вхождений его левой части выбрать любое.
На основании сказанного можно предположить, что существуют несколько различных выводов фиксированной цепочки из фиксированной цепочки . Далее на примерах мы убедимся в том, что это действительно так.
Введенные выше понятия непосредственной выводимости, выводимости и вывода можно уподобить известным из теории графов понятиям непосредственной достижимости, достижимости и пути (в ориентированных графах) соответственно. Мы специально выбирали обозначения выводимости близкими к тем, которые используют в теории графов. Нужно только заметить, что множество вершин графа конечно (по крайней мере, мы в этой книге изучаем только конечные графы), а множество слов бесконечно. Но графовая аналогия позволяет придать введенным выше понятиям большую наглядность: так, построение вывода в грамматике можно уподобить “путешествию” по некоторому пути в ориентированном графе.
Язык, порождаемый грамматикой
Центральным понятием в теории порождающих грамматик является понятие языка, порождаемого данной грамматикой.
Определение 7.8. Язык, порождаемый грамматикой , — это множество всех выводимых из аксиомы грамматики терминальных цепочек
Продолжая “игровую аналогию”, мы можем теперь сказать, что у “игры”, во-первых, появилась выделенная “начальная позиция”, аксиома; во-вторых, возникла “цель” — строя вывод в согласии с правилами грамматики, получить цепочку, не содержащую нетерминалов. Так, скажем, и в шахматах, мы начинаем игру не с произвольной позиции, а со вполне определенной начальной позиции, предусматривающей строго фиксированную расстановку фигур, а закончить игру стремимся в любой позиции, в которой вражескому королю поставлен мат. Но при этом нужно понимать, что правила вывода грамматики априори никак не связаны с ограничением строить выводы терминальных цепочек из аксиомы, а определяют возможность построения любого вывода, даже бесконечного.
Заметим также, что, согласно определению 7.8, нетерминалы не содержатся в цепочках языка, порождаемого грамматикой. Это совсем не значит, что нетерминалы “не нужны”, напротив, с их помощью мы организуем вывод и можем получить требуемые терминальные цепочки. Когда мы решаем математическую расчетную задачу и должны в результате получить число, то это не значит, что мы не должны пользоваться формулами. Но все буквенные обозначения в окончательном результате должны исчезнуть, хотя без них этот результат получить невозможно.
Эквивалентность грамматик
Определение 7.9. Две грамматики называются эквивалентными, если они порождают один и тот же язык.
Примем соглашение о следующей сокращенной записи правил с одинаковой левой частью: вместо записи
будем использовать запись , разделяя разные правые части вертикальной чертой.
Кроме того, рассматривая примеры грамматик, мы зачастую будем записывать только их множества правил вывода, избегая скрупулезной записи всего кортежа, определяющего грамматику. С учетом введенных выше соглашений об обозначениях это не должно приводить к недоразумениям.
Пример 7.4. Рассматриваемая ниже грамматика моделирует простейший фрагмент естественного (русского) языка: ее терминалы — это некоторые слова русского языка*, нетерминалами являются “грамматические категории”, а именно понятия “предложение”, “подлежащее” и “сказуемое” (они даны как слова, заключенные в угловые скобки):
= ({кот, пес, крокодил, мяукает, лает, плачет},{(предложение), (подлежащее), (сказуемое)},(предложение), ).
*Эти слова рассматриваются как неделимые символы, а именно буквы данного терминального алфавита. Нас никак не должно это смущать, ибо алфавит — это любое непустое конечное множество.
Множество правил имеет вид
(предложение) → (подлежащее) (сказуемое), (подлежащее) → кот | пес | крокодил, (сказуемое) → мяукает | лает | плачет.
Каждое правило вывода грамматики можно трактовать как определение той или иной грамматической категории: например, первое правило есть запись такого определения: “предложение — это подлежащее, за которым идет сказуемое”. Так как нас интересует только синтаксис языка, то это определение, касающееся исключительно записи предложения, есть требование, согласно которому, записывая предложение, сначала нужно поставить подлежащее, а после него — сказуемое. Другие правила грамматики определяют подобным образом “подлежащее” и “сказуемое”. Но тут новых грамматических категорий не возникает — просто перечисляются те слова, которые могут быть подлежащим и сказуемым.
Построим какой-нибудь вывод в грамматике :
(предложение)(подлежащее)(сказуемое) кот (сказуемое) кот лает.
Заметим, что “смысл” (семантика) выводимой цепочки нас никак не интересует. Мы вообще пока не знаем, что такое “смысл”. Так что кот может лаять, крокодил мяукать и т.д.
Грамматика как “система определений”
Приведенный пример, несмотря на его простоту, позволяет нам дать еще одну содержательную интерпретацию понятия грамматики. Грамматику можно рассматривать как “систему определений” некоторых “терминов”, “понятий”. Выделяется самое общее понятие (в данном примере понятие “предложение”), оно сводится к менее общим “понятиям” до тех пор, пока мы не придем к “конкретным объектам” (в данном случае к цепочкам в каком-то алфавите), подпадающим под определяемые “понятия”. Самое общее “понятие” — это аксиома, другие “понятия” — нетерминалы, “конкретные объекты” — терминальные цепочки. В подобном духе строится определение синтаксиса языков программирования с помощью так называемых форм Бэкуса — Наура. Пример такого описания приведен ниже (см. Д.8.2). За другими примерами следует также обратиться к литературе по языкам программирования.
Пример 7.5. а. Грамматика имеет множество правил
Нетрудно видеть, что данная грамматика порождает простейшие “идентификаторы” в алфавите, состоящем из двух “букв” и двух “цифр”, т.е. цепочки, начинающиеся обязательно с “буквы”.
Примеры выводов в грамматике
б. Грамматика порождает множество так называемых “правильных скобочных структур”*, например
*Мы заключили четверку, задающую грамматику , в угловые, а не в круглые скобки, чтобы не смешивать их со скобками, являющимися терминалами данной грамматики.
Полезно сопоставить с этой грамматикой индуктивное определение множества правильных скобочных структур: цепочка есть правильная скобочная структура; если известно, что цепочки и — правильные скобочные структуры, то цепочку также считаем правильной скобочной структурой; если известно, что — правильная скобочная структура, то и цепочка есть правильная скобочная структура; никаких правильных скобочных структур, кроме определенных выше, не существует. Видно, что грамматика есть не что иное, как форма записи сформулированного только что индуктивного определения: “правильная скобочная структура” (понятие, обозначенное аксиомой ) есть либо цепочка , либо “правильная скобочная структура” в скобках — , либо две “правильные скобочные структуры”, записанные одна после другой, — .
в. Рассмотрим грамматику с множеством правил вывода
Разберем некоторые примеры выводов: под символом будем указывать номер применяемого на данном шаге правила, а над символом — количество повторений этого правила:
Представим теперь вывод произвольной цепочки
(7.2)
Можно заметить, что посредством многократного применения правила (3) в выводе (7.2) происходит “перегонка” всех букв влево от всех букв , т.е. из цепочки , выводится цепочка . Если считать, что правило (3) на каждом шаге соответствующего вывода применяется так, что происходит замена первого вхождения цепочки цепочкой , то первый (самый левый) символ в цепочке “проходит” через один символ , следующему символу (в цепочке ) нужно пройти уже два символа и т.д. Отсюда следует, что правило (3) в указанном фрагменте вывода (7.2) применяется
раз.
Тогда последовательность номеров применяемых правил (протокол вывода) может быть записана так:
Гораздо труднее доказать, что данная грамматика порождает только цепочки указанного вида. Проанализируем другие варианты проведения вывода после применения правила (2) в выводе (7.2).
1. После применения правила (2) применяем правило (5) и после многократного применения правила (3) получаем
(7.3)
Цепочка, записанная последней, такова, что, во-первых, она не является терминальной, а во-вторых, к ней не применимо ни одно правило грамматики. Любая такая цепочка называется тупиковой.
2. Вывод (7.2) можно модифицировать, прервав на определенном шаге применение правила (3) и применив правило (4), где
Далее, если посредством применения правила (3) мы “перегоним” все символы влево, то получим цепочку , из которой легко получается цепочка . Таким образом, мы получили другой вариант вывода этой цепочки. Но если мы будем применять правило (4), начиная с цепочки
до тех пор, пока это возможно, то получим цепочку .
Из нее, если не применять сразу правило (5), снова получится цепочка . Применение же правила (5) приведет к тупиковой цепочке.
По аналогии можно рассмотреть произвольное чередование применения правил (3) и (4).
Все варианты вывода терминальной цепочки тем самым разобраны.
В общем же случае строгое доказательство того, что какая-либо предъявленная нам грамматика не порождает никаких других цепочек, кроме цепочек определенного вида, может быть весьма нетривиальным.
г. Зададим грамматику , множеством правил
Можно доказать, что данная грамматика порождает язык , т.е. все квадраты натуральных чисел, закодированных в однобуквенном алфавите.
При доказательстве утверждений о грамматиках нам будет удобно использовать одну процедуру, которую мы назовем переименованием нетерминалов грамматики.
Пусть дана какая-то грамматика . Введем новый алфавит , не пересекающийся с , так, что между алфавитами и установлено взаимно однозначное соответствие, при котором каждый символ переходит в символ . Построим новую грамматику , заменив каждое правило в исходной грамматике новым, в котором на месте каждого вхождения каждого нетерминала поставлен нетерминал . Нетрудно доказать, что построенная таким образом грамматика эквивалентна исходной. Говоря неформально, описанное преобразование грамматики состоит в переобозначении ее нетерминалов таким образом, что вместо каждого нетерминала ставится его “двойник”, причем “двойники”, соответствующие разным нетерминалам, различны.
Математический форум (помощь с решением задач, обсуждение вопросов по математике).
Если заметили ошибку, опечатку или есть предложения, напишите в комментариях.
21 / 1 / 1 Регистрация: 22.12.2013 Сообщений: 196 |
|
1 |
|
Составить грамматику, порождающую формальный язык23.11.2016, 16:09. Показов 13799. Ответов 5
1) составить грамматику, порождающую формальный язык L(G)={an bm ck| n, m, k>0} Добавлено через 37 минут Добавлено через 2 минуты
0 |
Programming Эксперт 94731 / 64177 / 26122 Регистрация: 12.04.2006 Сообщений: 116,782 |
23.11.2016, 16:09 |
Ответы с готовыми решениями: Составить грамматику, порождающую формальный язык 1) составить грамматику, порождающую формальный язык, заданный в… Составить грамматику, порождающую формальный язык Составить грамматику, порождающую формальный язык 1) составить грамматику, порождающую формальный… Составить грамматику, порождающую формальный язык Кто в этом шарит,помогите решением или дельным советом)… 5 |
4702 / 3358 / 1073 Регистрация: 01.09.2014 Сообщений: 9,237 |
|
23.11.2016, 22:32 |
2 |
Где правила грамматики?
0 |
21 / 1 / 1 Регистрация: 22.12.2013 Сообщений: 196 |
|
23.11.2016, 23:51 [ТС] |
3 |
Вот все правила грамматики с примерами
0 |
4702 / 3358 / 1073 Регистрация: 01.09.2014 Сообщений: 9,237 |
|
23.11.2016, 23:57 |
4 |
1) составить грамматику, порождающую формальный язык Вы в курсе, что частью грамматики является множество правил вывода? Какую информацию для читателя несет то, что вы обозначили в сообщении №1, пункт 1) это множество буквой P и не выписали само множество? Это все равно, что на задание “найти x” нарисовать стрелку, указывающую на букву x и написать: “Вот он!”.
1 |
21 / 1 / 1 Регистрация: 22.12.2013 Сообщений: 196 |
|
25.11.2016, 09:50 [ТС] |
5 |
3D Homer, я уже решил эту задачу, но спасибо за помощь)
0 |
0 / 0 / 0 Регистрация: 27.12.2021 Сообщений: 2 |
|||||
11.11.2022, 23:11 |
6 |
||||
привет! есть подробное решение как вы решали эту задачу? скиньте пожалуйста на почту [delete]
0 |
IT_Exp Эксперт 87844 / 49110 / 22898 Регистрация: 17.06.2006 Сообщений: 92,604 |
11.11.2022, 23:11 |
Помогаю со студенческими работами здесь Составить грамматику, порождающую формальный язык Составить грамматику, порождающую формальный язык Составить грамматику, порождающую формальный язык. Определить тип формальной грамматики и языка по классификации Построить грамматику, порождающую формальный язык 1) Построить грамматику, порождающую формальный язык. Искать еще темы с ответами Или воспользуйтесь поиском по форуму: 6 |
Говорят, что набор всех строк, которые могут быть получены из грамматики, является языком, сгенерированным из этой грамматики. Язык, генерируемый грамматикой G, представляет собой подмножество, формально определяемое как
L (G) = {W | W ∈ ∑ *, S ⇒ G W }
Если L (G1) = L (G2) , грамматика G1 эквивалентна грамматике G2 .
пример
Если есть грамматика
G: N = {S, A, B} T = {a, b} P = {S → AB, A → a, B → b}
Здесь S производит AB , и мы можем заменить A на a , а B на b . Здесь единственной принятой строкой является ab , т.е.
L (G) = {ab}
пример
Предположим, у нас есть следующая грамматика –
G: N = {S, A, B} T = {a, b} P = {S → AB, A → aA | a, B → bB | b}
Язык, генерируемый этой грамматикой –
L (G) = {ab, a 2 b, ab 2 , a 2 b 2 , ………}
= {a m b n | m ≥ 1 и n ≥ 1}
Построение грамматики, порождающей язык
Мы рассмотрим некоторые языки и преобразуем их в грамматику G, которая производит эти языки.
пример
Задача – Предположим, что L (G) = {a m b n | m ≥ 0 и n> 0}. Мы должны выяснить грамматику G, которая производит L (G) .
Решение
Поскольку L (G) = {a m b n | m ≥ 0 и n> 0}
набор принятых строк может быть переписан как –
L (G) = {b, ab, bb, aab, abb, …….}
Здесь начальный символ должен содержать хотя бы одну букву «b», которой предшествует любое число «a», включая ноль.
Чтобы принять набор строк {b, ab, bb, aab, abb, …….}, Мы взяли продукцию –
S → aS, S → B, B → b и B → bB
S → B → b (принято)
S → B → bB → bb (принято)
S → aS → aB → ab (принято)
S → aS → aaS → aaB → aab (принято)
S → aS → aB → abB → abb (принято)
Таким образом, мы можем доказать, что каждая отдельная строка в L (G) принята языком, сгенерированным производственным набором.
Отсюда и грамматика
G: ({S, A, B}, {a, b}, S, {S → aS | B, B → b | bB})
пример
Задача – Предположим, что L (G) = {a m b n | m> 0 и n ≥ 0}. Мы должны выяснить грамматику G, которая производит L (G).
Решение –
Поскольку L (G) = {a m b n | m> 0 и n ≥ 0}, набор принятых строк можно переписать как –
L (G) = {a, aa, ab, aaa, aab, abb, …….}
Здесь начальный символ должен содержать хотя бы одну букву «a», за которой следует любое число «b», включая ноль.
Чтобы принять набор строк {a, aa, ab, aaa, aab, abb, …….}, Мы взяли произведения:
S → aA, A → aA, A → B, B → bB, B → λ
S → aA → aB → aλ → a (принято)
S → aA → aaA → aaB → aaλ → aa (принято)
S → aA → aB → abB → abλ → ab (принято)
S → aA → aaA → aaaA → aaaB → aaaλ → aaa (принято)
S → aA → aaA → aaB → aabB → aabλ → aab (принято)
S → aA → aB → abB → abbB → abbλ → abb (принято)
Таким образом, мы можем доказать, что каждая отдельная строка в L (G) принята языком, сгенерированным производственным набором.
Отсюда и грамматика
G: ({S, A, B}, {a, b}, S, {S → aA, A → aA | B, B → λ | bB})
Теоретические
основы информатики
Расчетно-графическая
работа №4
Тема:
«Разработка формальной грамматики
Хомского».
-
Теоретическая
часть
Формальная
грамматика
Грамматика
языка – это набор средств для его
описания. Различают порождающие и
распознающие грамматики, в зависимости
от того, какая задача ставится: строить
новые цепочки языка или определять
принадлежит или нет некоторая цепочка
языку.
Формальной
порождающей грамматикой G называется
следующая совокупность четырех объектов:
где
VT
– терминальный алфавит (словарь); буквы
этого алфавита называются терминальными
символами; из них строятся цепочки
порождаемые грамматикой; VH
– нетерминальный, вспомогательный
алфавит (словарь); буквы этого алфавита
используются при построении цепочек;
они могут входить в промежуточные
цепочки, но не должны входить в результат
порождения; f
– начальный символ грамматики f
VH;
P
– множество правил вывода или порождающих
правил вида a→b
, где a
и b
– цепочки, построенные из букв алфавита
VT
и VH
, который называют полным алфавитом
(словарем) грамматики G.
Множество
конечных цепочек терминального алфавита
VT
грамматики G,
выводимых из начального символа f,
называется языком,
порождаемым
грамматикой G
и обозначается L(
G):
Пример
построения грамматики.
Примером
языка может являться множество различных
корректных формул, включающих в себя
натуральные числа, скобки и знаки
арифметических действий. Алфавитом в
этом случае будет множество Σ
=
{ 0, 1, 2, 3 , 4, 5, 6, 7, 8 , 9 , + , – , *, / , ( , ) }. Например,
строка (2+3)*(5+7) будет
словом такого языка, а строка +2(3-1( не
будет (хотя она и состоит из допустимых
символов, но записана с очевидными
ошибками). Как можно описать этот язык?
Мы знаем, что числа состоят из цифр и
могут объединяться с помощью знаков
арифметических действий в формулы
(например, формулой будет цепочка 2+3).
Формулы можно заключать в скобки, а
также складывать, вычитать, умножать и
делить — то есть объединять друг с
другом (и с числами) в более сложные
формулы. Например, (2+3)*(5+7) —
это сложная формула, состоящая из двух
более простых, ее можно символически
записать следующим образом: ФОРМУЛА
ЗНАК ФОРМУЛА.
Заметим, что вместо слов ФОРМУЛА можно
подставить любую корректную формулу,
а вместо слова ЗНАК —
знак любого арифметического действия,
и эта строка останется правильной
формулой.
Идея
теории порождающих формальных грамматик,
разработанной Н.
Хомским,
состоит в том, чтобы описывать язык
через правила подстановок, которые
позволяют строить слова языка из
«кирпичиков», описываемых с помощью
других «кирпичиков». Правила языка при
этом можно записывать, просто указывая,
какие цепочки «кирпичиков» можно
подставлять вместо других «кирпичиков».
Например, разобранное выше правило
построения сложных формул из простых
можно записать следующим образом (такая
запись называется правилом вывода)
1. ФОРМУЛА → ФОРМУЛА
ЗНАК ФОРМУЛА
Правило
нужно читать так: «вместо слова ФОРМУЛА можно
подставить цепочку ФОРМУЛА
ЗНАК ФОРМУЛА».
При этом вместо «кирпичика» ЗНАК можно
подставлять один из четырех символов
алфавита Σ
:
( +, – , * , / ). Это можно записать с помощью
следующих четырех правил.
2. ЗНАК→ +
3. ЗНАК→ –
4. ЗНАК→ *
5. ЗНАК→ /
Такая
запись обычно сокращается до следующей
(символ “|” не принадлежит Σ).
6. ЗНАК→ +
| – | * | /
«Кирпичики»,
обозначаемые нами большими буквами
(например, ЗНАК),
не являются символами рабочего
алфавита Σ (хотя
и могут быть на них заменены в процессе
подстановок). Они называются «нетерминалами»
(или «нетерминальными символами»), в
отличие от букв алфавита Σ,
называющихся терминалами. Смысл этих
названий состоит в том, что нетерминал
может быть заменен на цепочку других
символов (как в случае с нетерминалом ФОРМУЛА),
в то время как терминал уже ни на что
заменен быть не может и является
своеобразным «пунктном назначения»
наших замен (от англ. terminal — конечный
пункт). Чтобы получить слово языка, мы
должны все нетерминалы в конечном итоге
заменить на терминалы, а для этого в
описание языка должны входить
соответствующие правила.
В
рассмотренном примере для определения
нетерминала ЧИСЛО можно
ввести такие правила
7. ЧИСЛО→ ЦИФРА
| ЧИСЛО ЦИФРА
8. ЦИФРА → 0
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Иными
словами, если к числу приписать справа
еще одну цифру, то получится снова число.
Одно число — это также корректная
формула, что записывается следующим
образом:
9. ФОРМУЛА→ ЧИСЛО
Для
полного описания грамматики нам требуется
также разрешить заключать формулы в
скобки. Это делается с помощью правила
10. ФОРМУЛА→ (
ФОРМУЛА )
Надо
отметить, что нетерминал ФОРМУЛА в
нашем случае является выделенным —
он задает правильные формулы, то есть
слова нашего языка, в то время как,
например, нетерминал ЗНАК
не
задает слово языка. В таком случае
говорят, что нетерминал ФОРМУЛА является начальным.
Итак,
чтобы задать формальную грамматику,
требуется задать алфавиты терминалов
и нетерминалов, набор правил вывода, а
также выделить в множестве нетерминалов
начальный. Тогда словами языка, заданного
грамматикой, являются все слова, состоящие
только из терминалов, и при этом получаемые
из начального нетерминала по правилам
вывода.
Выведем
формулу (12+5) с
помощью перечисленных правил вывода:
ФОРМУЛА
(ФОРМУЛА)
(ФОРМУЛА
ЗНАК ФОРМУЛА)
(ФОРМУЛА
+ ФОРМУЛА)
(ФОРМУЛА
+ ЧИСЛО)
(ФОРМУЛА
+ ЦИФРА)
(ФОРМУЛА
+ 5)
(ЧИСЛО
+ 5)
(ЧИСЛО
ЦИФРА + 5)
(ЦИФРА ЦИФРА + 5)
(1
ЦИФРА+5)
(12+5)
Представление
грамматики в виде графа
Граф грамматики
в качестве вершин содержит цепочки,
выводимые из начального символа. Ниже
на рисунке дано представление грамматики
,
где
{a,b,c},
{S},
{S→
aSa
| bSb
| c}.
Классификация
формальных грамматик
Множество
формальных грамматик можно разложить
на классы в зависимости от ограничений,
накладываемых на продукции. Такие
ограничения формируют особые свойства
грамматик и особые методы анализа и
синтеза цепочек символов. Общепринятой
классификацией грамматик и порождаемых
ими языков является иерархия грамматик
Хомского, содержащая четыре типа
грамматик.
Грамматика
типа 0–
грамматика произвольного типа без
каких-либо ограничений на цепочки
символов. Продукции этой грамматики
имеют вид:
.
В
обеих частях продукции могут быть в
произвольном порядке и любом количестве
терминальные и нетерминальные символы,
т.е.
V*,
где верхним
левым индексом «*» обозначено множество
с пустым символом e,
а знак “::=”
означает: “…присвоить значение…”.
Такой
тип грамматики порождает множество
перечислимых языков, для которых
существует перечисляющий алгоритм.
Такой алгоритм может быть реализован
на машине Тьюринга, являющейся классической
моделью рекурсивной функции. Поэтому
такие языки называют также
рекурсивно-перечислимыми. Однако такой
тип грамматики не нашел применения в
языках программирования.
Пример
1. Пусть дана
грамматика G1
= < VT;
VH;
P; J > , где
VT
= { a; b } ; VH
= { A;B;C;J };
P
= { р1
: J ::= AbBb; р2
: J ::= AbJBb; р3
: Ab ::= Bba;
р4
: Bb ::= Cba;
р5
: Cb ::= ba } .
Какие
цепочки терминальных символов формирует
грамматика ?
Рассмотрим
левосторонний вывод:
J
=>AbBb =>BbaBb =>CbaaBb =>baaaBb =>baaaCba = (
ba3)(ba2);
J
=>AbJBb => BbaJBb => CbaaJBb => baaaJBb =>baaaAbBbBb
=> baaaBbaBbBb => baaaCbaaBbBb => baaabaaaBbBb =>
baaabaaaCbaBb =>
baaabaaabaaBb
=> baaabaaabaaCba => baaabaaabaabaa = (ba3)2(ba2)2;
J
=>AbJBb => BbaJBb => CbaaJBb => baaaJBb => baaaAbJBb
=> baaaBbaJBb =>…=> baaabaaabaaabaabaabaa = (ba3)3(ba2)3;
и т.д.
Грамматика
G1
формирует множество цепочек, используя
процедуру итерации:
L(G1)
= { (ba3)i
(ba2)i
| i = 1;2;3;… }.
Пример
2. Пусть дана
грамматика G2
= < VT;
VH;
P; J > , где
VT
= { a; b } ; VH
= { A;J } ;
P
= { р1
: J ::= JAa | b; р2
: aA ::= Aa ; р3
: bA ::= ab }
Какие
цепочки терминальных символов формирует
грамматика ?
Используем
также левосторонний вывод терминальных
цепочек:
J
=> b;
J
=> JAa => bAa => aba;
J
=> JAa => JAaAa => bAaAa => abaAa => abAaa => aabaa
= a2ba2;
J
=> JAa => JAaAa =>JAaAaAa => … => aaabaaa = a3ba3;
и
т.д.
Грамматика
G2
формирует множество цепочек, также
используя процедуру итерации:
L(G2)
= { aibai
| i = 0;1;2;3;… }.
Грамматика типа
1 – это
контекстно-зависимая грамматика или
грамматика непосредственных составляющих
(НС-грамматика). Второе название грамматики
объясняется тем, что любую цепочку можно
разложить на синтаксические составляющие
(члены предложения естественного языка)
и выполнить разбор каждой синтаксической
переменной (части речи естественного
языка) в составе синтаксической
составляющей. В естественных языках
для этого проводят грамматической
разбор структуры предложения. В языках
программирования – грамматический
разбор структуры программы, что всегда
необходимо при трансляции исходной
программы на язык вычислительной
машины.
Продукции этой
грамматики имеют вид:
1212
,
где AVH;
1,
2,
V*,
1
левый
контекст,
2
правый
контекст.
Каждый шаг вывода
состоит в замене одного вхождения
нетерминального символа вхождением
цепочки терминальных и/или нетерминальных
символов. Эта замена обусловлена наличием
левого и правого контекстов.
Для НС-грамматики
существенным является исполнение
условия:
1212
где |…| означает
длину цепочки символов, заключенных
между вертикальными линиями.
Это требует
исполнения в каждом правиле второго
условия:
.
Грамматики, в
которых все правила обладают этими
свойствами называют неукорачивающими.
Множество языков
НС-грамматики является собственным
подмножеством языков грамматики типа
0.
Для НС-грамматики
возможны случаи:
1) 11когда
2
;
2) 22
, когда
1
.
Для НС-грамматик
также допустим лево- и правосторонний
вывод, когда продукции грамматики
применяются к самому левому или самому
правому нетерминальному символу
анализируемой цепочки.
Пример
3. Пусть дана
грамматика G3
= < VT;
VH;
P; J > , где
VT
= { a; b; c} ; VH
=
{B; C; J } ;
P
= { р1
: J ::= aJBC | aBC; р2
: CB ::= DB;
р3
: DB ::= DC; р4
: DC ;;= BC; р15
: aB ::= ab;
р6
: bB ::= bb; р7
: bC ::= bc; р8
: cC ::= cc }.
Какие
цепочки терминальных символов формирует
грамматика ?
В
каждом правиле есть либо левый, либо
правый контексты.
Используем
также левосторонний вывод терминальных
цепочек:
J
=> aBC => abC => abc;
J
=> aJBC => aaBCBC => aabCBC =aabDBC => aabDCC =>
aabBCC => aabbCC => aabbcC => aabbcc = a2b2c2;
J
=> aJBC => aaJBCBC => … => aaabbbccc = a3b3c3
и т.д.
Грамматика
G3
формирует цепочки терминальных символов,
используя операцию итерации:
L(G3)
= { aibici
| i = 1;2;3;… }.
Грамматика типа
2 – это
контекстно-свободная грамматика
(КС-грамматика). Правила этой грамматики
не зависят от контекста, т.е. в левой
части каждого правила находится только
один нетерминальный символ, а в правой
части цепочка терминальных и/или
нетерминальных символов.
Продукции этой
грамматики имеют вид:
A ::= ,
где
V*.
Каждый шаг вывода
связан с заменой в цепочке одного
нетерминального символа на цепочку
терминальных и/или нетерминальных
символов.
Множество КС-языков
при выполнении условий
1
,
2
и
является
подмножеством НС-языков.
КС-грамматики
наиболее эффективно описывают состав
и структуру формального языка. Поэтому
они нашли применение в большинстве
языков программирования. Для унификации
языков программирования были разработаны
метаязыковые средства описания
синтаксических конструкций. Особое
место среди этих средств занимают
формулы Бэкуса-Наура (БНФ).
Для КС-грамматик
также удобен фиксированный способ
вывода (лево- или правосторонний ).
Пример
4. Пусть дана
грамматика G4
= < VT;
VN;
P; J > ,
где VT
= { буква; цифра} ; VH
= {A; B; J } ;
P
= { р1
: J ::= JA | JB | A;
р2
: A ::= буква;
р3
: B ::= цифра
}.
Какие
цепочки терминальных символов формирует
грамматики?
Для
удобства доказательства сохраним
левосторонний вывод:
J
=> A => буква;
J
=> JA => AA => буква буква;
J
=> JB => AB => буква цифра;
J
=> JA => JAA => AAA => … => буква буква
буква;
J
=> JA => JBA => ABA => … => буква цифра
буква;
J
=> JB => JAB => AAB => …=> буква буква
цифра;
J
=> JB => JBB => ABB => …=> буква цифра
цифра и т.д.
Грамматика
G4
формирует следующие цепочки терминальных
символов:
L(G4)
= { буква { буква | цифра } }.
Такова
грамматика для формирования идентификаторов
большинства языков программирования.
Грамматика типа
3 (регулярная)
– это линейная грамматика, правила
которой содержат в правой части не более
одного нетерминального символа.
Продукции этой
грамматики имеют вид:
A ::= 1B2
или
A ::=
где
1,
2,
V*VN.
Продукция линейной
грамматики называется праволинейной
(леволинейной), если единственный
нетерминальный символ в правой части
продукции всегда находится крайним
справа (слева).
Например,
А ::= 1B
– праволинейная
продукция,
A ::= B2
– леволинейная
продукция.
Если все продукции
грамматики праволинейные или леволинейные,
то грамматика также называется
праволинейной или леволинейной. Каждой
праволинейной грамматике соответствует
эквивалентная ей леволинейная грамматика.
В зависимости от типа грамматики
различают праволинейные и леволинейные
языки.
Языки линейной
грамматики представляют собственное
подмножество множества КС-языков.
Пример
5. Пусть дана
грамматика G5
= < VT;
VH;
P; J >
где VT
= {a; b; c } ; VH
= { A; B; J; } ;
P
= { р1
: J ::= Bb; р2
: A ::= Aa | a; р3
: B ::= Bb |
Aac | ac }.
Какие
цепочки терминальных символов формирует
грамматика ?
Можно
отметить, что все правила этой грамматики
-леволинейные. Проведем левосторонний
вывод терминальных цепочек:
J
Bb
acb
Aacb
aacb
acbb
Bbb
aacbb
Aacbb Aaacb
aaacb
aaacbb
Aaacb
Bbbb
acbbb
Грамматика
G5
формирует цепочки вида:
L(
G5
) = { aicbj
| i,j = 1;2;3;… }.
-
Примеры решения задач
Пример 1. Приведите
примеры не менее чем пяти языков в
алфавите:
Σ={a,b,c,d,e}
Пример
2. Построить
праволинейную грамматику для языка,
состоящего из 1 и 0 с четным числом нулей
и единиц.
Пример
3. Построить
КС-грамматику, порождающую правильно
расставленные скобки
Пример 4. Даны
грамматики G1 = ({0,1}, {A,S}, P1, S) и G2 = ({0,1}, {S},
P2, S).
P1:
S→0A1, 0A→00A1, A→
и
P2: S→0S1
| 01. Определить
языки грамматик и их типы.
Пример 5.
Построить
грамматику, порождающую язык L
= {anbncn
| n>0}.
Получить вывод терминальной цепочки
aaabbbccc.
-
Задание
Для выбранной в
соответствии с вариантом задания задачи:
-
Разработать
формальную грамматику Хомского G,
порождающую цепочки языка L(G). -
Построить
вывод заданной в варианте задания
цепочки. -
Представить
грамматику в виде графа. -
Определить
класс, к которому принадлежит грамматика. -
Сделать
выводы.
-
Содержание отчета
-
Определение
понятия «Формальная грамматика
Хомского». -
Определение
алфавита терминальных символов. -
Определение
алфавита нетерминальных символов. -
Определение
начального символа. -
Определение
правил вывода. -
Вывод
заданной цепочки языка. -
Разработка
графа грамматики. -
Определение
класса грамматики. -
Анализ
результатов и выводы.
-
Варианты задания
Ниже даны варианты
задания, в каждом из которых в качестве
способа описания формального языка
выбран алгебраический способ. Помимо
алгебраического описания языка вариант
задания включает цепочку, для которой
должен быть построен вывод в грамматике.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
L(G)
= {13n+2
0n
| n≥0}, 1111111100;
18.
L(G)
= {0n
1m
| n>m
и n,m
>0 }, 00000000011111;
19.
L(G)
= {
anb3mcma2n
| n>1
и m>1 },
aabbbbbbccaaaa.
-
Оформление ргр
РГР оформляются
на стандартных листах писчей бумаги с
использованием текстового редактора
и печатаются на принтере, кегль шрифта
Time
New
Roman
– 14. Отчет о РГР подшивается в папку.
Отчет о РГР начинается со стандартного
титульного листа на котором указывается
(сверху – вниз): учебное заведение,
кафедра, название и номер РГР, автор,
дата выполнения, проверяющий с указанием
ученой степени, ученого звания, фамилии
и имени, отчества, дата проверки, город,
год.
После титульного
листа следует содержание с указанием
разделов и станиц.
Далее идет текст
отчета с рисунками, таблицами, расчетами
разбитый на соответствующие заданию
разделы.
Далее следует
список использованной литературы.
Оформленные не
аккуратно отчеты о РГР возвращаются на
доработку.
Резолюция на
титульном листе проверенной преподавателем
РГР «Устранить
замечания»
требует от студента следующего:
1. Внимательно
ознакомиться с замечаниями преподавателя,
которые даны по тексту РГР;
2. Дать исправления
в специальном разделе в конце РГР «Работа
над замечаниями».
В этом разделе отмеченное преподавателем
место в тексте должно быть дано верно,
без ошибок;
3. Листы с ошибками
не удалять, текст РГР заново не
перепечатывать;
4. Сдать РГР на
повторную проверку.
Резолюция на
титульном листе РГР «Обратите
внимание на мои замечания»
требует от студента следующего:
1. Внимательно
ознакомиться с замечаниями преподавателя,
которые даны по тексту РГР;
2. Дать исправления
в специальном разделе в конце РГР «Работа
над замечаниями». В этом разделе
отмеченное преподавателем место в
тексте должно быть дано верно, без
ошибок;
3. Листы с ошибками
не удалять, текст РГР заново не
перепечатывать;
4. На повторную
проверку РГР не сдавать.