Gramatyka regularna

Z testwiki
Przejdź do nawigacji Przejdź do wyszukiwania

Gramatyka regularnagramatyka formalna, za pomocą której można opisać język regularny.

Istnieją dwa rodzaje gramatyk regularnych: gramatyka lewostronna, gramatyka prawostronna. Istnieje ścisły związek gramatyki lewostronnej oraz deterministycznego automatu skończonego, taki że gramatyka generuje dokładnie taki język, jaki akceptuje automat. Stąd gramatyki lewostronne generują dokładnie wszystkie języki regularne.

Gramatyka regularna to albo prawo- albo lewostronna.

Ważne ograniczenia postaci reguł

  • Po lewej stronie występuje zawsze dokładnie jeden symbol nieterminalny (tak samo jak w gramatykach bezkontekstowych)
  • Po prawej stronie występuje nie więcej niż jeden symbol nieterminalny i dowolny łańcuch symboli terminalnych. W gramatykach prawostronnie regularnych symbol terminalny występuje przed nieterminalnym (jeśli ten się tam znajduje), a w lewostronnie regularnych na odwrót[1].

Przykłady

poprawne reguły

AA
AB
AaA
AaB
Aϵ
Aa
ACa (reguła z gramatyki lewostronnie regularnej)

reguły niepoprawne

ABcD (dwa symbole nieterminalne z lewej strony)
ABC (dwa symbole nieterminalne z prawej strony)

Przykład gramatyki

Jako przykład z zakresu języków programowania może posłużyć gramatyka opisująca ciągi znaków będące zapisem liczb zmiennoprzecinkowych. Poniżej przedstawiono gramatykę G ze zbiorem reguł N = {S,A,B,C,D,E,F} i alfabetem Σ = {0,1,2,3,4,5,6,7,8,9,+,-,.,e}. Symbol S jest symbolem startowym, zaś przez ε oznaczamy ciąg pusty.

S → +A       A → 0A       B → 0C       C → 0C       D → +E       E → 0F       F → 0F
S → -A A → 1A B → 1C C → 1C D → -E E → 1F F → 1F
S → A A → 2A B → 2C C → 2C D → E E → 2F F → 2F
A → 3A B → 3C C → 3C E → 3F F → 3F
A → 4A B → 4C C → 4C E → 4F F → 4F
A → 5A B → 5C C → 5C E → 5F F → 5F
A → 6A B → 6C C → 6C E → 6F F → 6F
A → 7A B → 7C C → 7C E → 7F F → 7F
A → 8A B → 8C C → 8C E → 8F F → 8F
A → 9A B → 9C C → 9C E → 9F F → 9F
A →.B C → eD F → ε
A → B C → ε

Łączenie gramatyk

Za pomocą łączenia prawo i lewostronnych gramatyk można tworzyć języki, które niekoniecznie będą regularne, z definicji jednak, tego typu gramatyki nie będą już regularne.

Zaostrzanie zasad tworzenia reguł

Reguły te można zaostrzyć bez straty mocy, pozwalając na co najwyżej jeden symbol terminalny z prawej strony. Wprowadza się w tym celu kilka stanów pomocniczych Pi, i każdą regułę postaci:

Ac1c2c3ckB

Można przepisać do zestawu reguł:

Ac1P1
P1c2P2
P2c3P3
Pk1ckB

Analogicznie przepisuje się reguły postaci:

Ac1c2c3ck

Drugim często nakładanym ograniczeniem jest zabranianie reguł, które zmieniają stan bez czytania żadnych symboli:

AB

Algorytm pozbywania się ich jest następujący:

  • Wybieramy jedną regułę postaci AB, której nie oznaczyliśmy jeszcze jako zbędnej
  • Dla każdej reguły postaci BΓ, dodajemy do zbioru reguł regułę postaci AΓ, chyba że taka już istnieje
  • Jeśli B było akceptujące, A natomiast nie było, zaznaczamy A jako akceptujące
  • Zaznaczamy wybraną regułę jako zbędną – każde słowo, w którego wyprowadzeniu były użyte reguły ABΓ, możemy teraz wyprowadzić samym AΓ. Jeśli natomiast wyprowadzenie słowa kończyło się AB, gdzie B było akceptujące, możemy to wyprowadzenie zakończyć na A, które teraz również jest akceptujące.
  • Jeśli wszystkie reguły postaci AB są zaznaczone jako zbędne, usuwamy je i kończymy. Jeśli nie, wykonujemy kolejną iterację.

Zostają wtedy jedynie reguły postaci:

Aa
AaB
Aϵ

Z takich reguł bardzo łatwo już przejść do niedeterministycznego automatu skończonego.

Łagodzenie zasad budowania reguł

Można też pozwolić na łagodniejsze reguły – kierując się w stronę wyrażeń regularnych. Bez zmiany mocy gramatyk regularnych wolno dodać alternatywę, przy czym na symbole nieterminalne po prawej stronie nakłada się ograniczenie, że muszą one się znajdować na końcu dowolnej ścieżki wyboru alternatyw, np.:

Aa(B|aa(C|bD))

Przejście do tradycyjnych gramatyk regularnych dokonuje się, rozbijając każdą alternatywę na parę regułek, aż pozbędziemy się ostatniej:

AaB
Aaaa(C|bD)
AaaaC
AaaabD

Można też dodać gwiazdkę, oznaczającą powtórzenie fragmentu dowolnie wiele razy, o ile wewnątrz niej znajdują się tylko symbole terminalne, np.:

Aaa(ab)*bb(a)*B

Każdej gwiazdki pozbywa się, wprowadzając nieterminalnych symbol pomocniczy P, i dla regułki postaci Aαβ*γ tworzymy regułki:

AαP
PβP
Pγ

Na przykład:

Aaa(ab)*bb(a)*B
AaaP1
P1abP1
P1bbP2
P2aP2
P2B

Używając (z podanymi wyżej ograniczeniami) gwiazdki i alternatywy, możemy mieszać gramatyki regularne i wyrażenia regularne. Można też dla każdej gramatyki regularnej zbudować odpowiadające jej wyrażenie regularne. Konstrukcja jest następująca:

  • Przepisujemy gramatykę z użyciem alternatywy, tak żeby dla każdego symbolu nieterminalnego o regułkach AΓ1, AΓ2,..., AΓk pozostała tylko jedna regułka AΓ1|Γ2||Γk.
  • Wybieramy jeden symbol nieterminalny, oprócz symbolu startowego:
    • Jeśli w jego definicji znajduje się odwołanie do niego samego, przestawiamy te alternatywy do postaci A((Γ1||Γk)A)|(Γk+1||Γn) i zamieniamy jego definicję na A(Γ1||Γk)*(Γk+1||Γn)
    • Skoro w definicji A nie ma już żadnych odwołań do samego siebie, podstawiamy definicję A w miejsce wszystkich wystąpień A w innych regułkach. Otrzymujemy w ten sposób układ zawierający o jeden symbol mniej.
  • Na końcu zostaje nam tylko symbol startowy. Jeśli zawiera odwołanie do samego siebie, musimy go przekształcić zgodnie z tą samą procedurą.
  • Gramatyka jest postaci Swyrażenie regularne

Na przykład weźmy gramatykę słów nad alfabetem {a,b} nie zawierających podciągu baba. Gramatyka taka może mieć postać:

SaS – jak dotąd baba nie wystąpiło
SbPb – jak dotąd baba nie wystąpiło, podejrzany prefiks to b
PbbPb – jak dotąd baba nie wystąpiło, podejrzany prefiks to b
PbaPba – jak dotąd baba nie wystąpiło, podejrzany prefiks to ba
PbabPbab – jak dotąd baba nie wystąpiło, podejrzany prefiks to bab
PbaaS – jak dotąd baba nie wystąpiło
PbabbPb – jak dotąd baba nie wystąpiło, podejrzany prefiks to b
Sϵ – możemy skończyć, jeśli nie ma podejrzanego prefiksu
Pbϵ – możemy skończyć, jeśli podejrzany prefiks to b
Pbaϵ – możemy skończyć, jeśli podejrzany prefiks to ba
Pbabϵ – możemy skończyć, jeśli podejrzany prefiks to bab

Można ją przepisać do układu równań gramatycznych:

SaS|bPb|ϵ
PbbPb|aPba|ϵ
PbabPbab|aS|ϵ
PbabbPb|ϵ

Łatwo można się pozbyć Pbab:

SaS|bPb|ϵ
PbbPb|aPba|ϵ
Pbab(bPb|ϵ)|aS|ϵ

I Pba:

SaS|bPb|ϵ
PbbPb|a(b(bPb|ϵ)|aS|ϵ)|ϵ

Pb musiby sprowadzić do wygodnej postaci:

Pb((b|abb)Pb)|(ab|aaS|a|ϵ)

I pozbyć się odniesienia do siebie samego:

Pb(b|abb)*(ab|aaS|a|ϵ)

Możemy teraz podstawić do S:

SaS|b(b|abb)*(ab|aaS|a|ϵ)|ϵ

Teraz już tylko grupujemy samo-odniesienia:

S(a|b(b|abb)*aa)S|(b(b|abb)*(ab|a|ϵ)|ϵ)

I usuwamy je:

S(a|b(b|abb)*aa)*(b(b|abb)*(ab|a|ϵ)|ϵ)

W ten sposób mechanicznie stworzyliśmy wyrażenie regularne, którego ręczne zbudowanie byłoby znacznie trudniejsze.

Przypisy

Szablon:Przypisy

Szablon:Języki formalne i gramatyki