2011. május 17.

Számoljunk magyar nyelven

Következő írásomban magyar nyelven megfogalmazott alapvető számítási műveletek felismerésére és elvégzésére fogjuk megtanítani a számítógépünket. Az összeadás, a kivonás, a szorzás műveletét lesz képes felismerni és megadni az eredményt magyar nyelven. A tanításhoz a szabályalapú megközelítést fogunk használni és a Prolog programozási nyelvet.



Leírás

Maga a program nagyon szűk keretek között fog mozogni. Egytől ezerig fogjak felismerni a magyar nyelvű tőszámneveket[1] és ezeken fogjuk értelmezni az összeadás, a kivonás és a szorzás műveletén. Az osztást a szűk szótár miatt, ami csak a tőszámneveket tartalmazza, ki kellett hagynom az elemzésből. De a szótárbővítéssel és a szóképző szabályokkal a bemutatott analógia alapján könnyen elvégezhetjük sajátkezűleg.

Prolog

Maga az eszköz a Prolog (SWI-Prolog) programozási nyelv lesz. Erről már szóltunk itt a logikai programozással kapcsolatban, de az algoritmusok világában is szóba került. A Prolog lényegét talán egy mondatban is összefoglalhatjuk: amit képes elemezni, azt képes generálni is. Ezt a tulajdonságát fogjuk mi is kihasználni. Nem csak felismerni lesz képes, hogy egy adott karaktersorozat a magyar tőszámnevek része, hanem egy számot képes lesz lefordítani magyar nyelvre. Sőt, az aritmetikai műveletek implementálásával képes lesz műveleteket végezni a magyar nyelvű tőszámnevekkel, és az eredményt is magyarul adja meg. (Különlegességként még megemlíteném, hogy egy-egy számról képes lesz megmutatni, hogy hányféle magyar mondattal lehet előállítani.)

A szótár és a szabályhalmaz

Első feladat előállítani a tőszámnevek szótárát. Ezt két módon lehet. Egy, felsoroljuk az összeset. (Mivel megadtunk egy alsó és felső határt, ezért ez véges idő alatt és gépelési sebességétől függően lehetséges.) A másik módszer, hogy megkeressük a leírandó szótár szabályszerűségeit. A szabályszerűségeinek felismerése és betáplálásával, a szabályok felállításával ráhagyhatjuk a gépre, hogy előállítsa a megadott határok között előállítható összes grammatikus magyar nyelvű tőszámnevet.

Ehhez először a legkisebb, bonthatatlan elemeire kell szednünk a tőszámneveket. Továbbá a szótári elemeket egy kis kiegészítéssel is ellátjuk. A szótári elem mellett annak értékét is feltüntetjük. Ez a számítások elvégzése miatt szükséges és hogy a szóképzés útján előállított újabb tőszámnevekről el tudjuk dönteni, hogy mennyi az értékük.

/* Szótár */
d1(1) --> [egy].
d2(2) --> [kettő].
d3(2) --> [két].
d(3) --> [három].
d(4) --> [négy].
d(5) --> [öt].
d(6) --> [hat].
d(7) --> [hét].
d(8) --> [nyolc].
d(9) --> [kilenc].
t1(10) --> [tíz].
t1(20) --> [húsz].
t(30) --> [harminc].
t(40) --> [negyven].
t(50) --> [ötven].
t(60) --> [hatvan].
t(70) --> [hetven].
t(80) --> [nyolcvan].
t(90) --> [kilencven].
t2(10) --> [tizen].
t2(20) --> [huszon].
sz(100) --> [száz].
e(1000) --> [ezer].
view raw gistfile1.txt hosted with ❤ by GitHub

Aztán meg kell határoznunk azon szóképző szabályokat, amelyek a szótár elemeit felhasználva, azok kombinálásaival leírják a kívánt, 1-től 1000-ig tartó tőszámnevek tartományát.

/* Szóképző szabályok */
/*
* Nemterminálisból terminális szimbólum
* 1..99
*/
szam(X) --> d1(X).
szam(X) --> d2(X).
szam(X) --> d(X).
szam(X) --> t1(X).
szam(X) --> t(X).
szam(X) --> t2(Y),d1(Z), { X is Y + Z }.
szam(X) --> t2(Y),d2(Z), { X is Y + Z }.
szam(X) --> t2(Y),d(Z), { X is Y + Z }.
szam(X) --> t(Y),d1(Z), { X is Y + Z }.
szam(X) --> t(Y),d2(Z), { X is Y + Z }.
szam(X) --> t(Y),d(Z), { X is Y + Z }.
/* Nemterminálisból nemterminális szimbólum */
egytol_szaz(X) --> szam(X).
szaztol_ezer(X) --> egytol_szaz(X).
szaztol_ezer(X) --> szaz(X).
/*
* Nemterminálisból terminális
* 100...1000
*/
szaz(X) --> sz(X).
szaz(X) --> sz(Y),egytol_szaz(Z), { X is Y + Z }.
szaz(X) --> d3(Y),sz(Z), { X is Y * Z }.
szaz(X) --> d3(Y),sz(Z),egytol_szaz(Q), { X is Y * Z + Q }.
szaz(X) --> d(Y),sz(Z), { X is Y * Z }.
szaz(X) --> d(Y), sz(Z), egytol_szaz(Q), { X is Y * Z + Q }.
szaz(X) --> e(X).
/* Kiinduló állapota */
szamok(X) --> szaztol_ezer(X).
view raw gistfile1.txt hosted with ❤ by GitHub

Továbbá fel kell töltenünk azon plusz szabályokkal, amelyek képesek a műveletek felismerni és elvégezni.

/* Aritmetikai szabályok */
osszeadas(X) --> szamok(Y), [meg], szamok(Z), {X is Y + Z}.
kivonas(X) --> szamok(Y), [ből], szamok(Z), { X is Y - Z }.
szorzas(X) --> szamok(Y), [szer], szamok(Z), { X is Y * Z}.
view raw gistfile1.txt hosted with ❤ by GitHub

Eredmények tesztelése

A kettős pont az új eredmény kérését jelenti. Ha erre 'false' értéket kapunk, akkor nincs több ilyen érték. Ha van, akkor kiírja a következőt.
?- szamok(Értek,Magyarul,[]).
Értek = 1,
Magyarul = [egy] ;
Értek = 2,
Magyarul = [kettő] ;
Értek = 3,
Magyarul = [három]
?- szamok(123,Magyarul,[]).
Magyarul = [száz, huszon, három] ;
false.
?- szamok(Érték, [három,száz,nyolcvan,kilenc],[]).
Érték = 389 ;
false.
?- osszeadas(Eredmeny,[három,száz,tizen,kilenc,meg,negyven,nyolc],[]).
Eredmeny = 367 ;
false.
?- kivonas(180,Magyarul,[]).
Magyarul = [száz, kilencven, ből, tíz] ;
Magyarul = [száz, nyolcvan, egy, ből, egy] ;
Magyarul = [száz, kilencven, egy, ből, tizen, egy] .
?- szorzas(Eredmeny, [négy, szer, hetven, hét],[]).
Eredmeny = 308 ;
false.
view raw gistfile1.txt hosted with ❤ by GitHub

Hiányosságok

  • A magánhangzó-harmónia hiánya. A szorzás (-szor/-szer) és a kivonás (-ból/-ból) ragjai nem illeszkednek az első tőszámnévhez. Ez a könnyen orvosolható lenne, de a szabályokat és a szótárt is át kellene alakítani. Mivel ez inkább bemutató jellegű írás, így ettől eltekintettem.

  • Kétszer kettő”. Az eset különlegessége úgy foglalható össze, hogy a szorzás első részében helyes a 'két' alak a szó végén. Ezt nem képes kezelni a program. Mivel a számok/3 predikátum nem generálja és nem is ismeri fel tőszámnévként a 'két' alakot. Pedig a szorzás esetében ennek van értelme. (További példa: "Tizenkétszer kettő")

  • Továbbá a legnyilvánvalóbb hiányosság, a szótár és a szóképző szabályok nem teljesek. Azt viszont megemlíteném, hogy a szótárat két-három elemmel bővítve, a szóképző szabályokat hasonló analógiával bővítve egy öt perc alatt akár a 999 milliót is képes lenne kezelni a program...

Összefoglalás

Ebben az írásban egy szabályalapú nyelvleírásra mutattam példát. A magyar nyelv egy részhalmazát adtuk meg. Ez nem a teljes tőszámnevek halmaz, annak csak részhalmaza. De példát láthattunk arra, hogy a szóképző szabályokat milyen formában lehet megfogalmazni és a Prolog segítségével tesztelni, kipróbálni.

[1] A magyar tőszámnevek szabályalapú generálásának példáját Szécsényi Tibor: Nyelvtechnológia: a nyelv és a számítógép című szemináriumáról származik (Szegedi Tudományegyetem, 2009). Ezt a példát bővítettem ki azzal, hogy képes legyen ne csak felismerni, hanem műveletek is végezni magyar nyelven.

A teljes forráskód elérhető a Számítógépes Nyelvészet Github tárolójából.

Nincsenek megjegyzések: