Описание формальных грамматик
Итак, мы подошли к главному моменту изложения материала данного курса - описанию так называемых формальных грамматик. Важность формальных грамматик в обработке символьных данных, вообще в информатике сравнимо с важностью арифметики в математике, логики в философии и т.п. На принципах формальной грамматики проектируются и создаются новые языки программирования, пишутся программы для проверки орфографии и стиля, программируются "речевые" интерфейсы, создаются программы для коррекции ошибок и "оптимизации" выполнения программ. На основе формальной грамматики удалось "расшифровать" гармонию в шедеврах великих архитекторов, художников, композиторов. (Кстати говоря, большинство фуг И.С. Баха написаны так, что их порождает одна из грамматик)!
Формальная грамматика является частью алгебры и тесно связана с теорией групп и теорией автоматов. Но выводы формальной грамматики просты и понятны. Может быть, именно из-за этой "простоты" она "не в почете" ни у математиков, ни у гуманитариев. По крайней мере, по мнению автора, серьезных теоретических прорывов, начиная с середины 80-х годов прошлого века, в ней не наблюдается. Тем не менее, ее выводы в практике программирования применяются широко.
Выводы теории формальных грамматик используются в языках логического программирования (например, ПРОЛОГ) для построения деревьев вывода.
При чтении этого раздела Вам может быть потребуется обращаться как к предыдущим, так и последующим разделам. Более подробно материал из этого раздела представлен в [41].
10.1. Алфавит
Изучение любого языка человек начинает с азбуки. В формальной грамматике язык определяется вне зависимости от его смысла. Более того, один и тот же язык может формироваться несколькими грамматиками! Это как в школе - не так важен результат (который можно прочитать в конце учебника), как его получение - зафиксированное в тетради решение задачи. Поэтому подойдем к определению алфавита также формально.
О п р е д е л е н и е. Алфавит - это непустое конечное множество элементов.
В "классическом" языке алфавит - это набор литер. В фонетике - набор издаваемых человеком звуков речи. В музыке - это набор нот, и т.д.
С помощью алфавита часто возможно описать бесконечное множество слов. Совокупность всех слов, которую можно создать при помощи грамматики (иначе говоря, порождаемые грамматикой), называется языком. В отличие от алфавита язык может быть бесконечным.
Всякая конечная последовательность символов алфавита называется словом, или, более профессионально, цепочкой. Цепочками, состоящими из символов {a, b, c}, будут следующие последовательности: a, b, c, aa, ab, bc, ac, bb, abba и другие. Также допускается существование пустой цепочки Л - полное отсутствие символов. Важен также порядок следования символов в цепочке. Так, цепочки ab и ba - разные цепочки. Далее заглавные латинские буквы будут использованы как переменные и символы, а строчные латинские буквы будут обозначать цепочки. Например:
x = SVTЛистинг 10.1.
цепочка, состоящая из символов S, V и T, и именно в этом порядке.
О п р е д е л е н и е. Длиной цепочки называется число символов в этой цепочке. Она обозначается как |x|. Например: |Л| = 0, |A| = 1, |BA| = 2, |ABBA| = 4.
Если x и y являются цепочками, то их конкатенацией будет цепочка xy. От перестановки цепочек при конкатенации результат меняется (как и в теории групп). Если z = xy - цепочка, то x - голова, а y - хвост цепочки. Если нам безразлична голова цепочки, мы будем обозначать:
z = … xЛистинг 10.2.
а если нам безразличен хвост, мы будем писать:
z = x …Листинг 10.3.
О п р е де л е н и е. Произведение двух множеств цепочек определяется как конкатенация всех цепочек, входящих в эти множества. Например, если множество A = {a, b}, а B = {c,d}, то:
AB = {ac, ad, bc, bd}Листинг 10.4.
В произведении множеств, как и при конкатенации, порядок множителей существенен.
И при конкатенации цепочек, и при перемножении множеств цепочек истинным остается ассоциативный закон, записывающийся как:
z = (ab)c = a(bc) = abcЛистинг 10.5.
D = (AB)C = A(BC) = ABCЛистинг 10.6.
И, наконец, определим степень цепочки. Если x - непустая цепочка, то x0 = {Л}, x1 = x, x2 = xx, xn = x(x)(n-1). То же самое обстоит и со степенью множеств.
10.2. Терминальные и нетерминальные символы
Понятие терминальных и нетерминальных символов тесно связано с понятием правила подстановки (или продукции). Дадим его определение.
О п р е д е л е н и е. Продукцией, или правилом подстановки, называется упорядоченная пара ( U, x ), записываемая как:
U ::= xЛистинг 10.7.
где U - символ, а x - непустая конечная цепочка символов.
Символы, встречающиеся только в правой части, называются терминальными символами. Символы, встречающиеся и в левой, и в правой части правил, называются нетерминальными символами, или синтаксическими единицами языка. Множество нетерминальных символов обозначается как VN, а терминальных символов - VT.
О п р е д е л е н и е. Грамматикой G[Z] называют конечное, непустое множество правил, содержащее нетерминальный символ Z хотя бы один раз на множестве правил. Символ Z называют начальным символом. Далее мы все нетерминальные символы будем обозначать как <символ>.
[Пример 01]
<число> ::= <чс> <чс> ::= <цифра> <чс> ::= <чс><цифра> <цифра> ::= 0 <цифра> ::= 1 <цифра> ::= 2 <цифра> ::= 3 <цифра> ::= 4 <цифра> ::= 5 <цифра> ::= 6 <цифра> ::= 7 <цифра> ::= 8 <цифра> ::= 9
О п р е д е л е н и е. Цепочка v непосредственно порождает цепочку w, если:
v = x<U>y, а w = xuyЛистинг 10.8.
где <U> ::= u - правило грамматики. Это обозначается как v => w. Мы также говорим, что цепочка w непосредственно выводима из v. При этом цепочки x и y могут быть пустыми.
О п р е д е л е н и е. Говорят, что v порождает w, или w приводится к v, если существует конечная цепочка выводов u0, u1, …, u[n] (n > 0), такая, что
v = u0 => u1 => u2 => … => u[n] = wЛистинг 10.9.
Эта последовательность называется выводом длиной n, и обозначается v =>+ w. И, наконец, пишут:
v =>* w, если v => w или v =>+ wЛистинг 10.10.
10.3. Фразы
О п р е д е л е н и е. Пусть G[Z] - грамматика, x - цепочка. Тогда x называют сентенциальной формой, если <Z> =>* x. Предложение - это сентенциальная форма, состоящая только из терминальных символов. Язык - это подмножество множеств всех терминальных цепочек.
Далее следует важное определение, и читатель не должен читать раздел далее до тех пор, пока ему не будет полностью понятен смысл определения.
О п р е д е л е н и е. Пусть G[Z] - грамматика. И пусть w = xuy - сентенциальная форма. Тогда u называется фразой сентенциальной формы w для нетерминального символа <U>, если:
Z =>* x<U>y и <U> =>+ uЛистинг 10.11.
Если же
Z =>* x<U>y и <U> => uЛистинг 10.12.
то цепочка u называется простой фразой.
Следует быть осторожным с термином "фраза". Тот факт, что <U> =>+ u (цепочка u выводима из <U> ) вовсе не означает, что u является фразой сентенциальной формы x<U>y; необходима также выводимость цепочки x<U>y из начального символа грамматики Z.
В качестве иллюстрации фразы рассмотрим [Пример 01] сентенциальную форму <чс>1. Значит ли это, что символ <чс> является фразой, если существует правило: <число> ::= <чс>? Конечно же, нет, поскольку невозможен вывод цепочки: <число><1> - из начального символа: <число>. Какие же фразы сентенциальной формы <чс>1? Рассмотрим вывод:
<число> => <чс> => <чс><цифра> => <чс><1>Листинг 10.13.
Таким образом,
<число> =>* <чс> и <чс> =>+ <чс>1Листинг 10.14.
<число> => <чс><цифра> и <цифра> => 1Листинг 10.15.
Следовательно, <чс>1 и 1 - фразы. Простой же фразой будет только 1.
О п р е д е л е н и е. Основой всякой сентенциальной формы является ее самая левая простая фраза.
Грамматика из [примера 01] описывает бесконечный язык, то есть содержащий в себе бесконечное число предложений. Это объясняется тем, что грамматика содержит правило: <чс> ::= <чс><цифра>, то есть в некотором смысле символ <чс> сам себя определяет.
В общем случае, если:
<U> =>+ … <U> …Листинг 10.16.
мы говорим, что грамматика рекурсивна относительно символа <U>. Если же:
<U> =>+ <U> …Листинг 10.17.
то имеет место левая рекурсия, а если
<U> =>+ … <U>Листинг 10.18.
то имеет место правая рекурсия.
Если язык бесконечный, то определяющая его грамматика должна быть рекурсивной.
Отступление. Вообще, чем больше в грамматике рекурсивных правил, тем сложней и богаче язык и тем более сложные конструкции можно на нем описать. Это относится не только к языкам программирования, но и к "человеческим" языкам. Так, в одном из номеров журнала "Компьютерра" за 2007 год приводилась статья о языке одного бразильского индейского племени. В его языке почти отсутствовала рекурсия, характерная для большинства языков Европы и Азии. Это мешало индейцам из этого племени, например, воспринять Библию. Это позволило автору статьи сделать вывод, что чем более в языке народа рекурсии, тем более "прогрессивнее" и "цивилизованнее" народ. Автор статьи утверждает, что это пока лишь гипотеза, еще требующая подтверждения. Но эти выводы все равно подтверждают важность рекурсии при выборе грамматики.