Thursday, April 16, 2015

CF parsing módosított CYK-val

Emlékszünk, volt a Chomsky-normálforma, amiről már ranteltem egyet, hogy hogy kéne csinálni rendesen. Csak azt nem meséltem el, hogy mire jó, vagy miért.
A felállás most az, hogy szeretnénk CF nyelvet parsolni, vagyis ha van egy CF nyelvtanunk, és egy input stringünk, akkor egy derivációs fát akarunk építeni, aminek a határa épp ez az input string. Ezt pl. ha a nyelvtan Chomsky-normálformában van, akkor a Cocke-Younger-Kasami algoritmussal, avagy CYK, meg lehet tenni. Ha a nyelvtanunkban R a szabályok halmaza és n hosszú az input string, mondjuk w=a1a2an, akkor mindezt O(|R|n3) időben elvégzi, mégpedig dinamikus programozással:
  • kitölt egy T[i][j] táblázatot, ahol 1ijn;
  • a táblázat egy cellába a nemterminálisok V halmazának részhalmazai kerülnek;
  • az A nemterminális pontosan akkor kerül T[i][j]-be, ha Aaiai+1aj.
Nyilván akkor vezethető le a string, ha ST[1][n]. A táblázat kitöltésére pedig:
  • alapeset: T[i][i]={AV: AaiR}, tehát ha az i. betű a, akkor minden Aa szabály bal oldalát felvesszük.
  • indukció: T[i][j]-be akkor kerül be A, ha i<j, ha van olyan ikj index és ABC szabály, melyre BT[i][k] és CT[k+1][j]. Ehhez végig kell pörgetni a lehetséges k-kat és R-eket, tehát egy cella kitöltése (ji)|R|, ezt szummázva az összes cellára kapunk egy O(|R|n3) időigényt.
Persze ettől még csak az eldöntési problémát oldjuk meg, hogy a string benne van-e a nyelvben, nekünk viszont jó lenne egy parse tree is, ha igen. Ez nem baj, annyit kell csak tennünk, hogy letároljuk minden AT[i][j] esetén a k-t és az rR szabályt is, aki alapján bekerült az A ebbe a cellába (ha több ilyen A is van, az egyrészt baj, mert a nyelvtan nem egyértelmű, lásd később, másrészt válassz ki egyet), és az alapján visszaépíthető lesz a szokásos módon a fa is.

Remek, mondhatnánk, van egy elemző algoritmusunk, polinomidejű, mi kell még
hát például használható időigény
mert a köbös az nem az, bárki bármit mond.

Miről van szó, mikor akarunk elemezni? Mondjuk ha van egy fordítóprogramunk, akkor az benyeli a forráskódot tartalmazó file-t mint egy stringet és izzításképp felépíti belőle a kódhoz tartozó AST-t. De azért gondoljuk már végig, hogy ha pl a gcc köbös időben menne, egy ~10kbyte-os forrásfile-nál is cca 1012 ideig kéne várnunk, mire végez? Ez testvérek közt (egy ciklusban egy lépés, pár gigahertzes gépen) félóra minimum. File-onként. Indítasz egy buildet, másnapig be se jössz, vagy hogy? :D

Szóval, hatékonyabb elemzési módszer kell.

Ha visszagondolunk a rantemre a Chomsky normálformáról, akkor ott láttuk, hogy a balettlépések közül a darabolás és az ezUTÁN elvégzett ε-mentesítés lineáris időben, konstansszor nagyobb nyelvtant ad vissza, a szűk keresztmetszet a láncszabály-mentesítés volt, ami négyzetes.
öm...
de mégis kit zavarnak a láncszabályok?
csak jól kéne velük bánni.
Nézzünk már magunkba, ezzel a láncszabály-mentesítéssel csinálunk egy tök redundáns nagy nyelvtant. Kb annyi értelme, mint NFA-t ε-mentesíteni. Ott is csak az ε-lezárton kéne operálni on-the-fly, legalábbis az esetek túlnyomó részében, cachelve az elérhetőséget, még úgy is kevesebb tárat eszünk, mintha végrehajtanánk az ε-mentesítő algoritmust..

Tehát ha a nyelvtanunkban csak a hosszú jobboldalakat tördeljük, majd ε-mentesítünk, de a láncszabályokat békén hagyjuk (ha vannak esetleg nem használható nemterminálisok, azokat persze még az elején eldobjuk, amiből meg csak ε vezethető le, azokat töröljük a jobb oldalakról), akkor kapunk egy ekvivalens nyelvtant, amiben lesznek ABC, AB és Aa alakú szabályok. Erre az értelemszerűen módosított CYK:
  • T[i][i]-hez: ciklusban menjünk végig a nemterminálisokon. Ha AaiR, akkor vegyük be A-t T[i][i]-be és az összes olyan B-t is, melyre B-ből A levezethető láncszabályokkal;
  • T[i][j], i<j meghatározásához ciklusban menjünk végig a nemterminálisokon. Ha A még nem került bele T[i][j]-be, keressünk egy ABC szabályt és ikj indexet, amire BT[i][k] és CT[k+1][j]. Ha ilyen van, akkor tegyük bele A-t T[i][j]-be, és az összes olyan B-t is, melyre B-ből A levezethető láncszabályokkal.
A "levezethető láncszabályokkal" részt úgy oldjuk meg, hogy még elemzés előtt, a nyelvtannal együtt eltároljuk annak a transzponált láncszabály-gráfját (ne keress rá, most találtam ki ezt a nevet): csúcsok a nemterminálisok, BA akkor él, ha AB láncszabály a nyelvtanban. Ezután csinálhatunk egy optimalizálást: az erősen összefüggő komponenseket összerántjuk egyetlen nemterminálisba (mert pl. ha AB és BA is, akkor A-ból és B-ből teljesen ugyanazok a dolgok vezethetők le, minek nekik külön betű, és a nyelvtanunk is ambiguous lesz, kinek kell az), vagyis ennek a gráfnak a komponensgráfját elkészítjük, a nyelvtanunkban pedig minden nemterminálist átírunk a komponensére. Ettől az egész nyelvtan csak kisebb lehet, a letárolt gráf pedig egy körmentes (irányított ofc) gráf lesz, azaz egy DAG. Eztán ahányszor csak beteszünk valakit egy cellába, még be kell tegyük a belőle elérhetőeket ebben a gráfban, az egy darab mélységi bejárás, azzal a különbséggel, hogy ha egy már betett csúcsot érünk el, akkor annak a részfáját meg se kell nézzük, hiszen annak már minden csúcsát betettük, mikor az épp elért csúcsot is. Emiatt minden egyes nemterminálist csak egyszer járunk be, az időigény marad az |R|n3-ös, csak R lesz jó esetben kisebb. Persze a "nemterminálisokon végigmenni" is fordított toprend szerint érdemes, és akkor minden részfát egyszerre tudunk betenni, nem lesz fölöslegesen megjelölt kis részfa.
és akkor jó ez a módosított CYK, ami láncszabályt is támogat, csók a családnak. 

Miután ezt így kigondoltam, persze találtam egy 2009-es papírt, megjelent az Informatica Didactica c lapban (jé, van ilyen is), akik szintén. Ők ezt 2NF-nek hívják. ez egy ilyen szakma, az ember gondolkodik, aztán ha rájön bármire, rákeres és nahát, mások már megcsinálták :P Aztán rákerestem megint és más is implementálta ezt 2004-ben. Bizonyos dolgokat többször is elfogadnak tudományos helyeken. Nem kéne. Na mindegy..

örülünk, Vincent?
dehogy
hát ez ettől még köbös.
Legközelebb shift-reduce parserekről írok. Azok lineárisak, tehát gyorsak, de "persze" nem minden nyelvtanhoz van ilyen.

No comments:

Post a Comment