Főoldal
Főoldal
Jelentkezés a mozgalomba Diákszövetség Alapítvány Fórum Linkek GYIK
Azonosító
Jelszó
TÁMOP pályázat


A projekt az Európai Unió támogatásával,
az Európai Szociális Alap
társfinanszírozásával valósul meg.

Kutatók Éjszakája 2011

Együttműködő partnerünk

 

 

 

 

1911. District Magyarország

KutDiák Magazin
Prof. Csermely Péter blogja

 

Magyar Tehetségsegítő Szervezetek Szövetsége

Kutatásaink
Bővebben
Védnökök

Schmitt Pál, köztársasági elnök, a Mozgalom fővédnöke

Réthelyi Miklós, nemzeti erőforrás miniszter

Pálinkás József, az MTA elnöke

Hoffmann Rózsa, államtitkár

Védnökeink teljes listája

Mentoraink listája

Látogatók száma:
2547759
>> Kiemelkedő diákok >> >> >>
Csordás Róbert

TUDOK 2007 Veszprém Informatika szekció

PROLOGAPI

I. BEVEZETŐ
1.1. Általános rész
    A PrologAPI egy Prolog interpreterből és egy C++ interfészből áll, amely lehetővé teszi a Prolog forráskód használatát a C++-ban. A Prolog szabályokat közönséges eljárások hoz hasonlóan lehet használni a C++-ban. A Prolog forráskódot szövegként kell átadni az interpreternek, amely minden fontosabb C++ adattípust képes átalakítani Prolog típussá és fordítva. A használható típusok: szövegek (stringek), egész számok, lebegőpontos számok, logikai értékek, sorozatok és struktúrák. A teljes interpreter egy függvénykönyvtárban helyezkedik el (DLL Windows alatt és .so-ben (shared object) Linux alatt). Minden programnyelvvel használható csupán egy, a programba beépülő egység módosításával. Az interpreter Object Pascal-ban íródott (Delphi, FPC), az interfész pedig C++-ban.
1.2. A Prolog programnyelv
    A Prolog deklaratív programnyelv. A procedurális nyelvektől teljesen eltérő módon működik. A megoldandó feladatokat logikai mondatokkal kell leírni, és a Prolog ezt meg tudja oldani (ha az adott adatok alapján lehetséges).
    A logikai kifejezések lehetnek tények és szabályok. A tények leírják valamely objektum tulajdonságait (pl.: gyors(számítógép)). A szabályok több kérdésből álló rendszerek, amelyekkel le lehet írni bizonyos objetumok tulajdonságait, és különböző dolgok kérdezhetők le róla. (pl.: a  testvérek meghatározása egy családfában: testvér(X,Y):- anyja(X,An), anyja(Y,An), apja(X,Ap), apja (Y,Ap). Értelme: X testvére Y-nak, ha az X anyja ugyanaz mint az Y-é, és az X apja ugyanaz mint az Y-é). A vátozók nagy kezdőbetűkkel írandók, értéket csak egyszer kaphatnak. A kérdések hasonlítanak a procedurális nyelvekből megszokott eljáráshívásokhoz. A Prolognak feltett kérdések helyesek, ha létezik a kérdésnek megfelelő tétel vagy szabály, amelynek minden állítása igaz. Ha egy szabály nem bizonyul helyesnek, a Prolog megpróbál más megoldást találni rá. Ezt úgy tudja megtenni, hogy az előző kérdésekre más válaszokat próbál találni, hogy minden kérdés lehetőleg igaznak bizonyuljon. Ez a folyamat az ún. backtracking.
1.3. Prolog API
    A projektum célja, hogy elősegítse az olyan mesterséges inteligenciát használó programok írását, amelyeket nem lehet teljes egészében Prologban megírni. Ezek például a szakértői rendszerek (expert system), amelyeket valamely okból kifolyólag grafikai vagy más speciális kezelőfelülettel kell ellátni. A Prolog nem támogatja a 3D-t, így nagy segítségére lehet a játékkészítőknek is, akiknek más nyelvből kell megírniuk a játékot, viszont szeretnének benne mesterséges intelligenciát alkalmazni a számítógép által vezérelt játékosok számára. A PrologAPI segítségével bármely más nyelv összekapcsolható a Prologgal.
    A PrologAPI Windows és Linux alatt egyaránt használható. Sikeresen alakítja át a C++ adattípusokat Prolog típusokká és fordítva, az osztályok kivételével, ami nehezen kivitelezhető, ha egyáltalán lehetséges a Prolog eredeti elvének megbontása nélkül. A Prolog objektumok nem tartalmazhatnak metódusokat, váltóztatható tulajdonságokat, és sok más dologot, ami az objektum-orjentálts procedurális nyelvekben megszokott (Például nem készíthető számláló, amely minden egyes hívásakor növeli egy változó értékét, hiszen a változóknak már van értékük, és azt nem lehet váltóztatni). Az interpreter használható minden Arity/Prolog kompatibilis forráskóddal, jelenleg viszont még sokkal kevesebb beépített prediktummal (eljárással).
II. KIDOLGOZÁS
    A projektum több különböző eszköz segítségével készült el. A Prolog interpreter Delphiben íródott Windows alatt, és FPC-vel lett lefordítva Linuxra (SUSE 9.3). Az interfész a Microsoft Visual Studioban íródott Windows alatt és g++-ban (a GCC része) Linux alatt.
    A legnagyobb gond e projetumban az adattípusok konvetrálása és a kapcsolatteremtés a procedurális nyelvek és a Prolog között. Az interpreter úgy lett tervezve, hogy könnyen kapcsolatot tudjon teremteni más nyelvekkel.
    A PrologAPI három részből áll:
1.    Egy Prolog interpreterből, amely futtatja a Prolog programokat
2.    Egy, az argumentumok átalakításáért felelős részből
3.    Interfészből

A PrologAPI szerkezete
2.1. A Prolog interpreter
    A Prolog interpreter a PrologAPI legfontosabb része, úgyszólván az egész rendszer szíve. Úgy van tervezve, hogy könnyű legyen a procedurális nyelvek adattípusait az általa használt tokenekbe alakítani. A szabályokat, a procedurális nyelvekből megszokott módon, különálló egységekként kezeli. Könnyű őket hívni, és teljesen önállóak.
    A Prolog interpreter több elkülöníthető egységből áll:
1.    token-kezelő
2.    eljárás-kezelő
3.    változó-kezelő (minden szabálynak külön)
4.    operátor-kezelő
5.    backtraking-kezelő
6.    fő, irányító egység, amely összehangolja a többi egység működését
    A token-kezelő a forráskódból különböző szabályok alapján tokeneket generál. Az operátorokra vonatkozó szabályok a forráskódba építettek. A rendszer ezek alapján egy fa struktúrát állít össze a forráskódból. A tokeneknek van meghatározott fajtájuk, értékük, amely egy szövegkonstans, valamint altokenjei.
    Az eljárás-kezelő egy bázisban tárolja az összes tényt és szabályt. Minden kérdést ez az egység kezel. A kérdésnek megfelelő feladatokat elvégzi, majd minden kérdést, amelynek lehet más megoldása is, elküld a backrtaking-kezelőnek. Ez elmenti a saját bázisában, arra az esetre, ha esetleg más megoldást kellene keresni. Az eljárás-kezelő feladata az egyes kérdésekre más megoldást találni (ehhez szükséges az egyes változók felszabadítása is), és  jelezni az irányító egységnek, hogy futtassa a hozzájuk tartozó forráskódot. A gyors elérés érdekében a bázis bináris keresést használ.
    A változó-kezelő felelős a változók tárolásáért. Fő feladata a változók név szerinti keresése, és típusaik átalakítása, valamint az új vátozók tároljása a backrtacking-kezelő bázisában. A változók táblázata a gyorsabb hozzáférés érdekében szintén bináris keresést használ.
    Az operátor-kezelő feladata, hogy a szükséges műveleteket elvégezze a változókon. A különböző típusú operandusokat egyformára alakítja. Elöször kiválasztja a két operandus közül a kötöttebb típusút, majd a másik típusát is megpróbálja ugyanolyanra konvertálni (pl.: ha az egyik szöveg, a másik pedig egész szám, akkor a szöveget alakítja át egész számmá), ha nem sikerül, akkor fordítva (a számot alakítja szöveggé). Minden operátorhoz tartozik egy eljárás, amely a kiszámításáért felelős. Ezen eljárások néhány egyéb információval együtt egy konstans sorozatban helyezkednek el. Operátorszámításkor az operátor-kezelő e sorozatban megkeresi a megfelelő eljárást, és segítségével kiszámítja az eredményt.
    A backracking-kezelő a program bizonyos pontjain eltárolja az program állapotára vonatkozó adatokat, hogy backtracking esetén e pontoktól lehessen folytatni. Tárolja  az elvégzett kifejezésre vontatkozó adatokat, az újonnan lefoglalt változókat, a következő token címét, stb. Backtracking esetén az új változókat felszabadítja, hogy más értékeket kaphassanak, valamint módosítja a bázisban tárolt adatokat úgy, hogy a következő backtracking esetén egy harmadik megoldást találjon a rendszer (ha lehetséges).
    Az irányító-egységnek van a fő szerepe. Bejárja a forráskódból elkészített fa struktúrát, és sorban elvégzi a szükséges műveleteket. Meghatározza az elvégzendő művelet típusát, és aszerint közvetíti a feladatot a megfelelő egységnek (pl.: az eljárás- vagy pedig az operátor-kezelőnek). Ha valamely kérdést nem tudja megválaszolni a rendelkezésére álló adatok alapján, akkor a backtracking- és az eljárás-kezelő segítségével megpróbál más választ találni az előző kérdésekre (backtracking).  Ha a forráskód újabb kérdést tartalmaz, akkor az irányító-egység  fő eljárása rekurzívan hívja önmagát.
2.2. Az interfész – adatátvitel
    Az interfész elősegíti a PrologAPI könnyű használatát. Fő feladata az adattípusok koverziója, amely a következő lépésekből áll:
1.    Köztes típusokká alakításból – ez azért szükséges, mert a különböző programnyelvek kölönbözőképpen tárolják az adatokat a memóriában. Így az interpreter kommunikációja egységes, és lehetővé teszi az API haználatát bármely nyelvből az interpreter újrafordítása nélkül.
2.    A köztes adttípusok Prolog típusokká való konvertálásából, amelyeket az interpreter használni tud.
    A köztes típus egy adatstuktúra. A következő információkat tartalmazza:
1.    mutató a valós változóra (mutató)
2.    a változó típusa (bájt)
3.    kötöttség-indikátor (logikai)– megmutatja, hogy szabad-e a vátozó, azaz hogy a Prolog hozzárendelhet-e valamilyen értéket, vagy az már meghatározott
4.    ha sorozatról van szó, akkor a sorozat elemeinek a száma, egyébként 0 (integer)
5.    felszabadítás-indikátor (logikai)– ha az értéke igaz, akkor az Inerpreter felszabadítja a változó által használt memóriát
    Ha a kötöttség-indikátor értéke igaz, akkor a Prolog módosítani tudja a változó értékét. Ez jól használható a számítási eredmények visszaadására.    A sorozatok átviteléhez meg kell adni a mutatót a sorozat első elemére és a sorozat hosszát.
2.3. A struktúrák átvitele
    A struktúrák átvitele hasonló a közönséges változók átviteléhez. Az interfész létrehoz egy speciális adattípust (egy másik struktúrát), amelyhez hozzákapcsolja a struktúra minden elemének nevét és a hozzá tartozó köztes típusú adatot. Először meg kell határozni a struktúra minden elemének a címét a memóriában. Ez a memóriahatárhoz igazodás mértéke alapján (ALIGN) számítható ki. A konevrziónál szükség van minden elem típusának megadására.
    A struktúra elemeinek a címét meghatározó algoritmus:    
void** GetAddresses(void* thestruct, int argc, TVarType* types){
    char * bytemap=(char*) thestruct;

    void** result=new void*[argc];    

    int currpos=0;
    for (int a=0; a<argc; a++){
        datasize=SizeInBytes(types[a]);
        int currpage=currpos / ALIGN;
        int nextpage=(currpos+datasize) / ALIGN;
        if (currpage!=nextpage && currpage % ALIGN!=0){
            currpos= (currpage+1)*ALIGN;
        }
        
        *result[a]=&bytemap[a];
    }
    return result;
}
    Az eljárás argumentumai: mutató a struktúrára, az elemei száma és az elemek típusának sorozata. Visszatérési értéke az elemek címének megfelelő általános mutatósorozat.
2.4. Agrumentum-átalakító
    Az argumentum-átalakító a legfontosabb a Prolog más nyelvvel való összekapcsolásához. A köztes típusokat Prolog tokenekké alakítja, amelyeket az interpreter használni tud. A hívott eljárás nevét és a kötött változókat az argumentum-átalakító tokenekké konvertálja. Az üres változókat először átadja a változó-kezelőnek, majd a nevét a tokenbe írja. Ezután a változó és a megfelelő köztes típus párokat elmenti egy listába. E lista segítségével tudja később visszaalakítani a változókat köztes típussá. A változó típusát automatikusan a megadott típusra konvertálja.
2.5. A tények és szabályok eljárásokként való hívása
    A Prolog tények és szabályok egy eljárással hívhatóak más nyelvekből (kérdés a Prolognak). Ezen eljárás argumentumai: a hívott tény vagy szabály neve, az argumentumai száma és maguk az argumentumok. Tokeneket készít az argumentumokból,  és átadja az eljárás-kezelőnek, amely azt végrehajtja. Az eljárás igaz logikai értékkel tér vissza, ha megfelelő választ talál a kérdésre.
2.6. C++ eljárások használata a Prolog forráskódban
    A PrologAPI lehetővé teszi a C++ eljárások használatát is a Prologban. Ezen eljárásoknak cdecl híváskonverziót kell használniuk. Első lépésként meg kell adni az interpreternek, hogy mely funkciók állnak a rendelkezésére. Ehhez egy eljárást kell hívni, amelynek agumentumai: az eljárás neve, mutató az eljárásra és az eljárás argumentumainak típusai. Az eljárás átadja az adatokat az eljárás-kezelőnek, amely egy bázisba menti. A bázist minden hívásnál ellenőrzi, és ha megtalálja benne a megfelelő eljárást, akkor azt hívja. Ehhez először az argumentumait konvertálja C++ típusokká, azután azokat, amelyek kisebbek 4 bájtnál, kitölti nullákkal, majd a verembe helyezi őket. Ezután az ejlárás mutatójára ugrik (assemblyben call paranccsal).
III. A PrologAPI HASZNÁLATA
    Az interfész egy C++ osztályból és néhány eljárásból áll, amelyek lehetővé teszik az argumentumok átalakítását. Az osztály metódusai:
●    Konstruktor. Két argumentuma van, amely a képernyőn való szövegmegjelenítésért felelős. Stdcall típuskonverziót kell használniuk. Az első eljárás egész sor, a második pedig egy sornál rövidebb szöveg nyomtatására szolgál (writeLn és write). Automatikusan létrehozza az interpreter új példányát.
●    AddLine eljárás. A forráskód egy sorát átadja a Prolog interpreternek.
●    AddString eljárás. Formált szöveget ad át az interpreternek. A szöveg tartalmazhat újsor-indikátor karaktereket (' ').
●    Run eljárás. Teljes Prolog program futtatására használható.
●    CallFunction eljárás. A Prolog tények és szabályok eljárásként való hívására használható. Argumentumai: a tény vagy szabály neve, az agumentumok száma, és a köztes típusokká konvertált argumentumok (határozatlan számú, a cstdarg.h segítségével). Sikeres végrehajtés esetén igaz logikai értékkel tér vissza.
●    AddFunction eljárás. A C++ eljárásokat teszi elérhetővé a Prolog számára (a Prologból  hívhatóvá válnak). Argumentumai: az eljárás neve, mutató az eljárásra, az argumentumok száma, és az agumentumok típusai.
    A változók köztes típusokká való alakítását külön eljárások végzik. E funkciók mindegyike egy argumentumot tartalmaz, amely mutató az átvinni kívánt változóra. A változók megadhatóak üres változókként is, hogy a Prolog tudjon nekik értéket adni. Ez azért  szükséges, mert a Prolog egy eljárást több feladatra is használhat, és a szabad változók alapján dönti el, hogy mire fogja használni. Ezt egy másik argumentum segíti. Ha az értéke igaz, a változó szabad.
    A konstansok átalakítására másik eljárást kell használni. Hasonlóan használható mint a változóknál alkalmazott, a különbség csupán annyi, hogy az agumentum nem mutató, hanem maga a változó.
    A listák átalakítására két eljárás alkalmazható. Az egyik a konstans argumentumok, a másik pedik a változóak esetében használható. Argumentumai: a lista elemeinek száma és az elemeknek megfelelő köztes típusok sorozata.
    A sorozatok konvertálására egy függvény használható, melynek argumentumai az elemek száma és maga a sorozat (mutató a sorozat első elemére).
    A struktúrák konvertálása valamivel összetettebb folyamat. A konvertáló eljárás argumentumai: mutató a struktúrára, a struktúra neve, elemeinek száma, és legalább egy struktúraelem-leíró. A struktúraelem-leíró egy különleges struktúra, amely az elem nevét, típusát és az elem kötöttségi indikátorát (azaz azt, hogy szabad-e a változó) tartalmazza. Minden fontosabb típus használható.
IV. ÖSSZEFOGLALÁS
    Ez a projektum leegyszerűsíti az olyan programok írását, amelyeket könnyű megírni Prologban, és valamilyen oknál fogva (pl.: a kód egyes részei nem írhatóak meg Prologban, mint a 3D-s kezelőfelület, a portok olvasása és írása, stb.) mégis procedurális nyelvben kell implementáni. Sikeresen működik az egyszerű adattípusokkal, sorozatokkal és struktúrákkal. Még kevés beépített eljárást tartalmaz, de ez elegendő algoritmusok írásához. Könnyű használni és stabilan működik, bár a hiba-ellenőrzése még nem tökéletes.
    4.1. Jövőbeni tervek:
●    Az osztályok átvitele. Az osztályok átvitelét a C++ eljárások hívása segítségével lehetne megoldani, mivel némely eljárásokat nem lehet implementálni Prologban. Be kellene vezetni a valódi változókat, amelyek értéke tetszőlegesen váltóztatható, attól viszont már a Prolog is vesztene az értelméből. Ez viszont felvet elég sok problémát. Az osztályokat struktúraként lehetne átvinni, és a struktúrát használni a metódusokban argumentumként (a fordítók is ezt teszik). Az osztályok változóit a futásidejű típusinformáció (RTTI – Run Time Type Information) segítségével lehetne meghatározni.
●    Beépített eljárások (prediktumok) bővítése, az Arity, Turbo i Strawberry Prologgal való teljes kompatibilitás érdekében
●    A függvénykönyvtárak dinamikus beolvasása és funkcióinak használata
●    Interfészek elkészítése más nyelvekhez is (Pascal, Java, stb.)
Felhasznált irodalom:
●    Bjelica Vavić: Prolog – A programozás alapjai.  Požarevac: Braničevo.  1988.
●    Tóth Bertalan:  Programozzunk C++ nyelven! Budapest: ComputerBooks. 2003.

Keresés
Támogatóink
Címünk

Kutató Diákok Országos Szövetsége
1149 Budapest, Várna u. 12-14.

Tel/Fax: 1-222-0517

Várjuk 1%-os felajánlásaikat a Kutató Diákokért Alapítvány részére. Köszönjük! 

Az Alapítvány számlaszáma: 11705008-20463306

Az Alapítvány adószáma: 18235331-1-42

Külföldi kapcsolataink

Tisztségviselők
Kövess minket a Facebookon!

Új ügyességi és kaland játék került fel az oldalra, melyhez mindenkinek jó szórakozást kívánunk!
Mentor - Magyar csillagok


Egyetemen Kutató Diákoknak
Országos Tudományos Diákköri Tanács

Súgó