Generatywizm a informatyka

Generatywizm jako lingwistyczna teoria jest najbliższy teoretyczno-informatycznemu sposobowi patrzenia na język. To, co najbardziej łączy programistów i językoznawców, to postać Naoma Chomsky'ego i jego pomysł na opis gramatyk. 

UWAGA: tu zaczyna się część informatyczna; nie jest taka straszna, ale jeśli chcesz ją pominąć, przewiń post trochę niżej, do kolejnej uwagi

W informatyce, język to zbiór słów nad jakimś alfabetem. Zapisujemy to w następujący sposób: $L \subset \Sigma^*$, gdzie $L$ to język, a $\Sigma$ to alfabet - zbiór znaków. Gwiazdka na alfabecie oznacza, że wybieramy z niego słowa (ciągi znaków) o dowolnej skończonej długości. Znaczek $\subset$ oznacza podzbiór, czyli wybrane słowa ze wszystkich możliwych. 

Język możemy w zgrabny sposób opisać za pomocą gramatyki. W zależności od tego jak skomplikowany jest on do wyznaczenia, będziemy potrzebować jej innego rodzaju. Hierarchia Chomskiego, z której korzystają informatycy, wyróżnia cztery typy gramatyk:


  • typ 3, reprezentujący języki regularne, które możemy opisać za pomocą gramatyk regularnych lub automatów skończonych
  • typ 2, reprezentujący języki bezkontekstowe, które możemy opisać za pomocą gramatyk bezkontekstowych lub niedeterministycznych automatów ze stosem
  • typ 1, reprezentujący języki kontekstowe, z którymi wiążemy gramatyki kontekstowe i automaty z ograniczoną liniowo taśmą
  • typ 0, reprezentujący języki rekurencyjnie przeliczalne, z którymi wiążemy gramatyki ogólne (kombinatoryczne) i maszyny Turinga



Do opisu składni języków programowania wykorzystywana jest gramatyka bezkontekstowa (nie przejmujemy się otoczeniem przy produkowaniu wyrazów). Składa się ona z czterech elementów:

  • $\Sigma$ - wspomnianego wcześniej alfabetu, który będziemy nazywać zbiorem symboli terminalnych (tworzenie wyrazów z języka będzie polegało na przepisywaniu symboli; terminalnych (czyli końcowych) już nie możemy zamienić na inne)
  • $\Gamma$ - zbioru symboli nieterminalnych (te będziemy mogli, a nawet chcieli przepisywać)
  • $\delta$ - reguł przepisywania, czyli tzw. produkcji
  • $S$ - symbolu startowego, od którego zaczynamy przepisywanie

Przykład 1 (trochę abstrakcyjny, jeśli czujesz się przeciążony nadmiarem matematycznych znaczków, możesz go pominąć)

Załóżmy, że nasz alfabet składa się z dwóch elementów: $\Sigma = \{a,b\}$, a zbiór nieterminali z trzech: $\Gamma= \{A,B,S\}$. Jako reguły przepisywania (zamieniania nieterminali na ciągi terminali i innych nieterminali) przyjmijmy:

$S \to ASB$
$S \to \epsilon$ ($\epsilon$ oznacza słowo puste, czyli 0-literowe)
$A \to a$
$B \to b$

"A" możemy zamienić tylko na "a" - to jest taki producent literek "a". Analogicznie, nieterminal "B" to producent literek "b". "S" to nasz symbol startowy. Możemy albo zamienić go na ciąg "ASB", albo skończyć proces przepisywania i zamienić go na słowo puste.

Przykładowy ciąg zamian (wywód) może wyglądać w następujący sposób:

$S \to ASB \to AASBB \to AAASBBB \to AAABBB \to aAABBB \to ... \to aaabbb$

Jak widać na tym przykładzie, powyższa gramatyka produkuje język składający się ze słów postaci a...ab...b, gdzie ilość literek "a" musi być równa ilości literek "b".

Przykład 2 (mniej abstrakcyjny - dotyczy języków programowania)

Tworzenie ciągów aaabbb nie jest zbyt pasjonujące. Zobaczmy więc bardziej praktyczne zastosowanie - wykorzystanie gramatyk bezkontekstowych do opisu języków programowania. Jeśli nie mieliście styczności z żadnym językiem programowania wcześniej, to w dużym uproszczeniu słowa w takich językach, czyli programy komputerowe, wyglądają jak lista kolejnych rozkazów dla maszyny. 



Kilka bardzo prostych przykładowych produkcji z takich gramatyk:

$VARIABLE\_DEFINITION \to "var" NAME$
$VARIABLE\_ASSIGNMENT \to NAME "="  NUMBER$
$INSTRUCTION \to VARIABLE\_DEFINITION$
$INSTRUCTION \to VARIABLE\_ASSIGNMENT $
$BLOCK \to BLOCK$ $INSTRUCTION$
$CONDITION \to NAME "==" NUMBER$
$WHILE \to "while"$ $CONDITION$ $"do"$ $BLOCK ";"$

Powyższa gramatyka pozwala tworzyć zmienne o określonej nazwie, przypisywać im liczby i porównywać wartości zmiennych z wartościami liczb. Daje również możliwość stworzenia pętli "while", która zwykle semantycznie oznacza, że chcemy wykonywać jakiś blok poleceń dopóki jakiś warunek jest spełniony.

UWAGA: uff, tutaj kończy się część informatyczna

Okazuje się, że gramatykę języka naturalnego można (próbować) opisać w bardzo podobny sposób: za pomocą symboli nieterminalnych (tutaj nazywanych preterminalnymi) i reguł ich przepisywania. 

Najbardziej podstawowy generatywny model zawiera następujące symbole preterminalne:

$NP$ - nominal phrase, czyli fraza rzeczownikowa
$N$ - noun, czyli rzeczownik
$VP$ - verb phrase, czyli fraza czasownikowa
$V$ - verb, czyli czasownik
$AdjP$ - adjective phrase, czyli fraza przymiotnikowa
$Adj$ - adjective , czyli przymiotnik
i oczywiście symbol startowy $S$ - sentence, czyli zdanie.

Powyższe symbole możemy przepisywać za pomocą następujących reguł:

$S \to NP$ $VP$ (każde zdanie musi się składać z części związanej z podmiotem i orzeczeniem)
$NP \to AdjP$ $N$ (rzeczownik może mieć swoje określenie w postaci frazy przymiotnikowej)
$VP \to V$ $NP$ (czasownik może mieć swoje dopełnienie w postaci frazy rzeczownikowej)

oraz trywialnych:

$NP \to N$ 
$VP \to V$
$AdjP \to Adj$  

Ponieważ zapisywanie rozkładu zdania jako ciągu przepisywań jest dość nieczytelne, często do jego reprezentacji wykorzystuje się drzewka (w informatyce również, są to tzw. drzewa wywodu albo drzewa parsowania):

Na powyższym drzewku jako symbole terminalne zaznaczyłam słowa, które już mają uzgodnioną swoją ostateczną formę (np. "lubi" zamiast "LUBIĆ"). Ustalanie tej formy następuje jednak dopiero w dalszych etapach. 

W ogólności proces generowania zdania w takiej gramatyce możemy zapisać w następujący sposób:

1. Przepisywanie symboli preterminalnych
2. Ustalanie form (np. osoby, liczby, rodzaju) i sprawdzenie, czy dane słowa mogą być ze sobą zestawione
3. Transformacje (np. zmiana szyku)

Jak widzicie generatywny opis języka jest więc bardzo podobny do sposobu, w który informatycy opisują swoje języki - albo dość abstrakcyjne i czysto teoretyczne, albo np. języki programowania. Nie jest to jednak jedyne połączenie tych dwóch dziedzin nauki, mam nadzieję, że w którymś z kolejnych postów uda mi się przedstawić Wam inne ciekawe styki tyki światów.



Komentarze