Programozás | Prolog » Markusz Zsuzsa - Prologban programozni könnyű

Alapadatok

Év, oldalszám:1996, 102 oldal

Nyelv:magyar

Letöltések száma:899

Feltöltve:2004. október 21.

Méret:478 KB

Intézmény:
-

Megjegyzés:

Csatolmány:-

Letöltés PDF-ben:Kérlek jelentkezz be!



Értékelések

Nincs még értékelés. Legyél Te az első!


Tartalmi kivonat

BEVEZETÉS • A PROLOG újelvû, a matematikai logikán alapuló nagyon magas szintû nyelv. Az újelvû kifejezést ma már felváltja a PROLOG-szerû, ugyanis a PROLOG lett a legnépszerûbb azok közül a programozási nyelvek közül, amelyek azt tûzték ki célul, hogy a számítógép alkalmazkodjon az ember igényeihez, és ne megfordítva [1]. A PROLOG beépített következtetési rendszere olyan intelligens és korszerû programozási elveket enged meg, amelyek merõben eltérnek a hagyományos gyakorlattól. • 1976 óta írok PROLOG programokat, de még mindig emlékszem arra az örömre, amikor az elsõ programom (egy nagyon egyszerû építész tervezõ program) szinte azonnal mûködni kezdett. Építõmérnöki és alkalmazott matematikai háttérrel rendelkeztem, de programozni semmilyen tisztességes nyelven nem tudtam (akkoriban a "tisztességes" nyelvek az ALGOL, a FORTRAN és a COBOL voltak). Pár hónapja kerültem a NIM IGÜSZI Software-fejlesztési

osztályára a Németi István által vezetett Mesterséges Intelligencia csoportba. Belecsöppentem a mechanikus tételbizonyítás, a szemantikus programhelyesség és az újelvû nyelvek világába, a matematikai logika és az univerzális algebra volt az elméleti eszközünk. Régóta meghirdetett célja volt csoportunknak, hogy Magyarországon is lehessen PROLOG-szerû nyelven programozni [1,2]. Németi István hozta el az elsõ PROLOG interpretert Edinburghból [3], amit persze nem sikerült felélesztenünk. Talán ez is inspirálta Szeredi Pétert, hogy megírja az elsõ magyar PROLOG interpretert CDL nyelven (Compiler Description Language) [4]. Mi persze azonnal kipróbáltuk, hogy mit tud ez a csoda nyelv, vajon beváltja-e azokat a reményeket, amelyeket az újelvû programozási nyelvekkel szemben az emberekben ébresztettünk. Én azóta is a legszívesebben PROLOG-ban programozok • Miért szeretjük ezt a nyelvet? Én azért, mért sokkal közelebb áll az emberi

gondolkodáshoz, a logikához, ezért a természetes nyelvhez is, mint a többi ún. algoritmikus hagyományos programozási nyelv (beleértve a ma divatos PASCAL és C nyelveket is). A PROLOG megpróbálja sok számítógép felhasználó álmát megvalósítani, mégpedig azt, hogy ne kelljen egy probléma megoldásához a megoldó algoritmust részletesen leírni, elég legyen a feladatot és a feladat feltételrendszerét definiálni, és a számítógépes rendszer oldja meg a problémát, ahogy tudja. Azaz legyen intelligens Nem állítom, hogy a PROLOG ezt a magas igényt maradéktalanul teljesíti, de legalább megteszi az elsõ lépéseket. És nekem sokkal szimpatikusabb egy nyelv, ha azt mondhatom el róla, hogy az emberi logika szabályaiból áll, amelyeket már a régi görögök is ismertek, nem pedig a számítógépre szabott speciális utasítások (értékadás, if then else, ciklus, go to utasítások stb.) sorozatából • A 70-es évek közepe óta a PROLOG-ot

sikeresen alkalmazzák a robot kutatás, a természetes nyelv megértése, a programhelyesség és a tételbizonyítás területein. Japánban az ötödik generációs számítógép kutatásokat is nagy részben a PROLOG-ra alapozzák. Az elmúlt években a nyelv alkalmazási területe rohamosan bõvült: építészeti és gépészeti tervezõ rendszereket, szimulációs programokat és rendkívül sokfajta ún. szakértõi rendszert írnak PROLOG-ban Magyarországon és szerte a világon. • Ez a könyv azoknak szól, akik szeretnék megismerni a PROLOG programozás alapelveit, azokat a tulajdonságokat, amelyek újelvû vagy nagyon magas szintû nyelvvé teszik. Némi számítástechnikai kultúrát feltételezünk, de ez a könyv kezdõknek szól. A PROLOG ugyan a matematikai logikára épül, logikai programozásnak is szokták nevezni, de nem fogunk többet használni a logikából, csak annyit, amennyi a "józan paraszti ész" logikájába is belefér, és amennyivel

minden programozó vagy mérnök kell, hogy rendelkezzen. Ez a könyv nem a számítógép tudósoknak, hanem a majdani PROLOG alkalmazóknak készülõ. • PROLOG oktatási gyakorlatom alapján meggyõzõdtem arról, hogy példaprogramokon keresztül lehet a legkönnyebben programozni tanulni. Ezért a könyv minden tudományos bevezetés (jelölések, definíciók, tételek) helyett közöl tizenkét példaprogramot, amelyeket részletesen elmagyaráz. A tucat példaprogram és a hozzájuk fûzött magyarázat alkotja a könyv tizenkét fejezetét. A programok egyszerûek, egyik sem hosszabb egy oldalnál, és úgy vannak összeválogatva, hogy felöleljék a PROLOG programozás legfontosabb és legjellemzõbb tulajdonságait és eszközeit. Mindegyik tartalmaz egy-két új programozási eszközt, módszert vagy trükköt, amelyet külön alfejezet tárgyal részletesen. Ez a PROLOG oktatási módszer akkor alakult ki, amikor 1983/84-ben Kanadában, a Calgary Egyetem Számítógép

Tudományi Tanszékén, majd ezt követõen 1985-ben Angliában a Polytechnic of the South Bank, London, Elektro-Mérnöki Tanszékén mesterséges intelligencia kurzusokat vezettem. Itt szeretnék köszönetet mondani minden diákomnak, akik kérdéseikkel, megjegyzéseikkel sokat fejlesztettek ezen a módszeren. Az eredetileg angolul megírt programokat a könnyebb érthetõség kedvéért magyarra lefordítva közöljük. Sajnos az ékezeteket nem lehet kitenni a PROLOG programok szövegében, ezért az Olvasók elnézését kérem, de azt hiszem, hogy az angolul nem tudó magyar Olvasónak még így is sokkal egyszerûbb lesz megérteni a programokat, mint hogyha gyakran kellene szótárt használnia. Minden fejezetet három vagy négy gyakorlat zár le, amelyeket a program megértése után bárki megpróbálhat megoldani. Az I. Függelék tartalmazza valamennyi gyakorlat megoldását • A könyv programokat a Számítástechnikai Kutató és Innovációs Központ által

kifejlesztett MPROLOG-ban írtuk [14,15], és egy IBM XT konfiguráción futtattuk. Az MPROLOG moduláris PROLOG-ot jelent Nagy alkalmazói PROLOG programok modulokra való bontása elõsegíti a hierarchikus programtervezést. Oktatóprogramjaink természetesen olyan kicsik, hogy mindegyik egy modulnyi. Kivételt képez az utolsó,12 program, ahol éppen az volt a cél, hogy a modulok kapcsolatát bemutassuk, ezért három modulból áll. Tehát a könyv elsõ 11 fejezetében egyáltalán nem fogunk foglalkozni a modulokkal, gyakorlatilag bármely PROLOG implementáció tartalmazza azokat a tulajdonságokat, amelyeket tárgyalni fogunk. A 12 fejezet viszont kimondottan az MPROLOG modulszerkezetét kívánja megismertetni az Olvasóval. A könyv II Függeléke az IBM PC MPROLOG beépített eljárásait közli, a III Függelék pedig röviden leírja, hogyan is kell az MPROLOG-ot használni IBM PC környezetben. Azoknak az olvasóknak nyújtván segítséget, akik ki akarják próbálni

PROLOG tudásukat számítógépen. A IV Függelék az angol kifejezések magyar fordítását tartalmazza. Ez a 12 program természetesen nem reprezentálja a PROLOG adta összes lehetõséget. Távolról sem De azt oktatási tapasztalataim alapján bízvást állíthatom, hogy aki végigolvasva ezt a könyvet részleteiben megérti a 12 program mûködését, és meg tudja csinálni a fejezetek végén lévõ gyakorlatokat, az nyugodtan elkezdhet programozni PROLOG-ban. • Rendkívül hálás vagyok Luigi Marcolungo barátomnak, aki felbíztatott arra, hogy megírjam ezt a könyvet, dr. John Cleary és dr. Ágnes Kaposi kanadai, illetve londoni kollégáknak, akik a Mesterséges Intelligencia kurzusok keretein belül lehetõséget adtak a példaprogramok megírására, tesztelésére és az oktatásban való kipróbálására. Sok köszönettel tartozom Németi Istvánnak, aki a logika és logikai programozás iránti szeretetet oltotta belém és elindított azon az úton, amelynek

egyik állomása ez a könyv. Köszönet illeti munkahelyem, a Magyar Tudományos Akadémia Számítástechnikai és Automatizálási Kutató Intézetét, hogy az elmúlt évek során számomra lehetõvé tette, hogy a PROLOG programozással kapcsolatos elméleti és gyakorlati munkákba elmélyülhettem. Köszönet illeti Dömölki Bálintot, a Számítástechnikai Kutató és Innovációs Központ Elméleti Laboratóriumának vezetõjét, aki kezdettõl fogva segíti és irányítja az MPROLOG-gal kapcsolatos kutató, fejlesztõ és alkalmazói munkákat. Hálával tartozom a kitûnõ MPROLOG implementáció megvalósítóinak: Szeredi Péternek, Köves Péternek, Farkas Zsuzsának, valamint Domán Andrásnak, aki az MPROLOG grafikát fejlesztette ki. Sok barátom és kollégám nyújtott nagy segítséget a könyv kéziratához fûzött értékes megjegyzéseikkel, javaslataikkal, ezért köszönet illeti Márkus Gábort, Almásy Gedeont, Kálmán Györgyöt, Sántáné Tóth

Editet, Dévai Ferencet és Sándor Gábort. Külön szeretném megköszönni Futó Ivánnak azt a lelkiismeretes és gondos munkát, amellyel ezt a könyvet lektorálta. Végül köszönet illeti Stuiber Zsuzsát a szellemes illusztrációkért. Márkusz Zsuzsanna Budapest,1987 l. PROGRAM: FAGYLALT module fagylalt. body. fagylalt:vasarol(Fagylalt), nl, write("Menj a boltba es vegyel "), write(Fagylalt), write(" fagylaltot!"), nl. fagylalt:otthon marad, nl, write("Maradj otthon es egyel "), write("olyan fagylaltot, ami van!"), nl. vasarol(Fagylalt):finom(Fagylalt), kaphato(Fagylalt). finom(rum). finom(tutti frutti). finom(csoki). finom(eper). finom(malna). kaphato(vanilia). kaphato(csoki). kaphato(malna). kaphato(tutti frutti). otthon marad. endmod /* fagylalt /. Programfutások: ? fagylalt. Menj a boltba es vegyel tutti frutti fagylaltot! Yes ? fagylalt, fail. Menj a boltba es vegyel tutti frutti fagylaltot! Menj a boltba es vegyel csoki

fagylaltot! Menj a boltba es vegyel malna fagylaltot! Maradj otthon es egyel olyan fagylaltot, ami van! NO l.l A program célja • Az elsõ program a fagylaltvásárlás témakörében nyújt segítséget. Ha begépeljük a ? fagylalt. feladatot, a program kiírja, hogy milyen fagylaltot vásároljunk. 1.2 A feladat feltételrendszere • Ebben a programban a fagylalt beszerzésének két módja van: vagy elmegyünk a boltba és kiválasztunk egy fagylaltot, vagy otthon maradunk és kivesszük a hûtõszekrénybõl azt a fagylaltot, ami van. Ahhoz, hogy ki tudjuk választani a megfelelõ fagylaltot, tudnunk kell, melyik fagylalt finom és kapható-e. A program ismeri az ízlésünket, és számon tartja a finom fagylaltokat, valamint azt is tudja, hogy pontosan mely fagylaltok kaphatók. A program tudásához az is hozzátartozik, hogy otthon maradni minden feltétel nélkül lehet. 1.3 A program részei, egy PROLOG program alapelemei • A program elsõ, második és utolsó sora

a program modul elejét és végét definiálja. Ez a három sor olyan MPROLOG sajátosság, amivel egyenlõre nem kell törõdnünk. • A PROLOG program mondatok sorozatából áll, amelyeket pont zár le. E mondatokat PROLOG állításoknak is szoktuk nevezni. Minden mondat egy logikai állítás (v y a logikában használatos terminológiával: egy formula), amelynek igazságértéke van. Kétféle igazságérték van: igaz és hamis, de egy PROLOG mindig igaz Például a finom(tutti frutti). azt jelenti, hogy a tutti frutti finom fagylalt. Ha gondosan végignézzük a programot, láthatjuk, hogy kétfajta PROLOG állítás szerepel a programban: Az elsõ háromban szerepel a :szimbólum, a többiben nem. Ez a szimbólum a logikai implikáció, a nyíl (←) MPROLOG-beli megfelelõje, a vesszõ pedig a logikai és (^) jele. A vasarol(Fagylalt):- finom(Fagylalt), kaphato(Fagylalt). PROLOG állítást könnyen átírhatjuk a logikában szokásos jelölésekkel a következõ

formulává: vasarol(Fagylalt) ^ finom(Fagylalt) ^ kaphato(Fagylalt) azaz a Fagylalt-ot megvásároljuk, ha a Fagylalt finom és a Fagylalt kapható. Szinte mindegyik PROLOG implementációban a nagybetûvel írt azonosítók változót, a kisbetûvel írottak konstanst jelölnek. Ebben a programban a Fagylalt az egyetlen változó Az implikáció tulajdonképpen egy feltételes igazságot rögzít. Ha a nyilat a fenti példa szerinti irányban használjuk, akkor a nyíl jobb oldalán lévõ részt feltételnek, a bal oldalán levõ részt következménynek hívjuk. Példánkban a feltétel két elemi állítást (vagy atomi formulát) tartalmaz: finom(Fagylalt), kaphato(Fagylalt) amelyek logikai ÉS-sel vannak összekapcsolva. A jobb oldal elemi állításait (kicsit pontatlanul) feltételeknek is fogjuk nevezni. A következmény a PROLOG állításokban mindig csak egyetlen elemi állítás, jelen esetben: vasarol(Fagylalt) Egy elemi állítás egy predikátum névbõl és az ezt

követõ zárójeleken belüli néhány argumentumból áll, ahol minden argumentum egy kifejezés. Egy argumentumú predikátum például a finom, és nulla argumentumú predikátum például az otthon marad. Több argumentumú predikátumokra a késõbbi programokban fogunk példát látni • Az egyszerû kifejezés vagy egy változó (pl.: Fagylalt), vagy egy konstans (azonosító vagy szám, pl: tutti frutti) Összetett kifejezésekrõl a 6., 9 és 10 fejezetekben fogunk részletesen írni • A logikai implikáció formájú PROLOG állításokat szabályoknak nevezzük. Egy szabály bal oldala egy elemi állítást, a jobb oldala több elemi állítást is tartalmazhat vesszõvel elválasztva. A szabály bal oldalát a szabály fejének, a jobb oldalát a szabály törzsének is szoktuk nevezni. A feltétel nélküli PROLOG állításokat tényállításoknak nevezzük A FAGYLALT példaprogramunkban tehát az elsõ három PROLOG állítás szabály, az összes többi

tényállítás. l.4 A feladat világa, definíciók, célállítások • A szabályokkal és tényállításokkal tulajdonképpen definiáljuk a feladat világát. Nézzük meg például, hogy mit is jelent a finom predikátumú tényállítások sorozata? Itt egyszerûen felsoroljuk a finomnak ítélt fagylaltokat. És mi a helyzet azokkal a fagylaltokkal, amelyek kimaradtak? Például a kókusz fagylalttal? Hát a kókusz fagylalt nem finom fagylalt, nem is kapható, teljesen kimaradt a világunkból, mintha nem is létezne, nem tudunk róla semmit. Még azt sem tudjuk róla, hogy rossz fagylalt, hiszen akkor azt is hozzá kellett volna venni a programhoz! Egy PROLOG program egy leszûkített világban mûködik, azokat és csak azokat a körülményeket veszi figyelembe, amelyeket a programmal definiáltunk. Ezért is hívjuk az azonos predikátumú tényállítások összességét definíciónak Például a kaphato(vanilia). kaphato(csoki). kaphato(malna). kaphato(tutti frutti).

tényállítások összessége a kapható fagylaltokat definiálja. Hasonló módon, azon szabályok összességét, amelyeknek bal oldala azonos predikátumú, szintén definícióknak nevezzük. Például a következõ két szabály: fagylalt:vasarol(Fagylalt), nl, write("Menj a boltba es vegyel "), write(Fagylalt), write(" fagylaltot!"), nl. fagylalt:otthon marad, nl, write("Maradj otthon es egyel olyan fagylaltot, ami van!"), nl. a fagylalt beszerzésének módjait definiálja. Tehát egy PROLOG program egyszerûen definíciók sorozata, amelyek segítségével leírjuk a feladat világát. De hogyan lesz ebbõl program, hogy fog mûködni? • Egy PROLOG program futtatásához kell egy program (amilyen pl. a FAGYLALT programunk), egy feladat, amelyet egy vagy több elemi állítás formájában fogalmazunk meg, és szükség van egy feladatmegoldó algoritmusra vagy következtetési rendszerre, amely a PROLOG rendszerbe van beépítve. Egy PROLOG

programozási rendszer éppen attól nagyon magas szintû vagy intelligens, hogy saját következtetési rendszerrel, feladatmegoldó stratégiával rendelkezik, amely leveszi a teher egy részét a programozó válláról. Ezzel a stratégiával a programozónak nincs dolga, ezt már kitalálták, beprogramozták, mûködik. Hogy mennyire hatékony, az fõleg a feladat jellegétõl függ • PROLOG terminológiában egy feladat kitûzése egy célállítás megfogalmazását jelenti. Egy célállítás egy vagy több elemi állítás vesszõvel elválasztva, amelyeket pont zár le. Egy célállítást megadhatunk kérdõjellel vagy anélkül Ezzel megismerkedtünk a PROLOG állítások három osztályával: szabály, tényállítás, célállítás. Tehát PROLOG program = tények + szabályok + célállítás A program végrehajtása egy célállítás bizonyítása. 1.5 PROLOG programfutások eredményei • Egy PROLOG program futásának három eredménye lehet: 1. A feladatot

sikerült megoldani Ha a célállítás változókat is tartalmaz, az MPROLOG rendszer kiírja a változók értékeit, majd kiírja hogy Yes és leáll. 2. A feladatot nem sikerült megoldani Az MPROLOG rendszer kiírja hogy NO és leáll 3. A program fut, fut, fut, és nem akar leállni Ha az összes tár- és híváskorlátot elértük, akkor persze leáll a program a megfelelõ hibaüzenettel. • Ez utóbbi feladatmegoldási "eredménynek" a programozók nem szoktak örülni. A hagyományos nyelveken programozók azt mondanák, hogy bizonyára végtelen ciklusba esett a PROLOG futtató rendszere, de ez korántsem biztos. Ha a program fut, és nem akar leállni, akkor nem tudhatjuk, hogy azért nem áll le, mert végtelen ciklusba esett, vagy csak azért, mert olyan bonyolult a feladat, hogy még két hét futásra volna szükség a sikeres megoldásához. Ez utóbbi jelenséget néha kombinatorikai robbanásnak is szoktuk nevezni. Bármily furcsa, mindhárom

programleállási jelenség tökéletes összhangban van a PROLOG programozás matematikai logikai hátterével, tehát törvény létezik. Aki elolvassa a megfelelõ logikai szakkönyveket, az megtudhatja, hogy milyen összefüggés van az automatikus tételbizonyítás elmélete és a PROLOG programozás gyakorlata között [9]. Természetesen minden programozó igyekszik elkerülni a végtelen ciklust és a kombinatorikai robbanást, a megfelelõ módszerekre a késõbbiek során még visszatérünk. 1.6 Egy feladat megoldása: egy célállítás végrehajtása • Nézzük meg, hogy hogyan is történik egy feladat megoldása PROLOG-ban, illetve mi is a módja egy célállítás végrehajtásának? Nyomon fogjuk követni azt az utat, amelyet a PROLOG rendszer automatikusan bejár. • A PROLOG rendszer betöltése után be kell olvasnunk a programot. A beolvasás rendszerint szintaktikai ellenõrzéssel folyik, és hibaüzeneteket is kaphatunk, ha a PROLOG állítások formája nem

felel meg az éppen aktuális megvalósítás szintaktikai követelményeinek. Ha a programot sikeresen beolvastuk, begépelhetünk egy célállítást, például ? fagylalt. A feladat megoldása úgy kezdõdik, hogy a PROLOG következtetési mechanizmusa megkeresi a fagylalt definícióját, és megpróbálja a feladatot egyeztetni a definícióval. Ez az egyeztetés akkor érdekes, ha a szóbanforgó predikátumnak van egy vagy több - argumentuma Ezt az egyeztetést a PROLOG az ún mintaillesztéssel hajtja végre, amelyrõl késõbb részletesen fogunk beszélni. • A mi példánkban a fagylalt nulla argumentumú predikátum, tehát az egyeztetés problémamentes. De felmerülhet a kérdés, hogy egy definíció két állítása közül melyiket válasszuk, hiszen vannak olyan definíciók, amelyek még több PROLOG állításból állnak. A válasz roppant egyszerû: a PROLOG a lehetséges közül mindig az elsõt próbálja illeszteni, és ha ez a mintaillesztés szabályai

szerint nem sikerül, akkor megpróbálja a következõt illeszteni, és így tovább. Esetünkben tehát a ? fagylalt. célállítás és a fagylalt:vasarol(Fagylalt), nl, write("Menj a boltba es vegyel "), write(Fagylalt), write(" fagylaltot!"), nl. szabály automatikusan illeszkedik. Ennek a szabálynak a jobb oldala feltételként hat elemi állítást tartalmaz Ezt úgy is mondhatjuk, hogy a fagylalt feladatot csak úgy tudjuk megoldani, ha megoldjuk a vasarol(Fagylalt) részfeladatot, és megoldjuk a soremelés (nl), és a kiírás (write) részfeladatait az adott sorrendben. Tehát egy feladat megoldását részfeladatok megoldására vezettük vissza. Elõször az elsõ, vasarol(Fagylalt) részfeladattal kell foglalkoznunk. Eljárásunk ugyanaz, mint az eredeti feladatnál, keresünk hozzá egy definíciót: vasarol(Fagylalt):finom(Fagylalt), kaphato(Fagylalt). amely történetesen megint egy szabály két feltétellel. Ez a feladat egy egyargumentumú

predikátumot tartalmaz, így itt a mintaillesztés már lényeges lehet. Tehát itt az ideje, hogy leírjuk, hogyan hajtjuk végre a mintaillesztést, egyszerû kifejezések esetén. 1.7 Mintaillesztés, visszalépés • A mintaillesztésnél mindig két elemi állítást próbálunk illeszteni, az egyik egy cél (vagy feladat), a másik egy definícióhoz tartozó szabály bal oldala vagy egy tényállítás. A két elemi állítás predikátum neve és argumentumszáma megegyezik, a kérdés csak az, hogy az argumentumok kifejezései illeszthetõk-e. Azt már említettük, hogy az egyszerû kifejezés vagy egy változó, vagy egy konstans (azaz azonosító vagy szám). Az illesztés három szabálya a következõ: l. Két változó mindig illeszthetõ 2. Egy változó és egy konstans mindig illeszthetõ 3. Két konstans csak akkor illeszthetõ, ha megegyeznek Ez három szabály így is összefoglalható: két egyszerû kifejezés akkor és csak nem illeszthetõ, ha különbözõ

konstansok. Például: • Fagylalt és Fagylalt Y és APA Fagylalt és rum rum és tutti frutti 8 és csoki rum és rum illeszthetõ, mert két változó; illeszthetõ, mert két változó; illeszthetõ, mert az egyik változó, a második pedig konstans; nem illeszthetõ, mert két különbözõ konstans; nem illeszthetõ, mert két különbözõ konstans; illeszthetõ, mert azonos konstansok. Ezek után nézzük meg, hogy mi történik akkor, ha illeszteni akarjuk a vasarol(Fagylalt) részfeladatot a vasarol(Fagylalt):finom(Fagylalt), kaphato(Fagylalt) szabály bal oldalával. Az illesztés természetesen sikerül, hiszen a vasarol predikátumnak egy argumentuma van és az mindkét elemi állításban változó (véletlenül mindkettõben Fagylalt). Úgyhogy most a vasarol(Fagylalt) feladatot két részfeladatra bontottuk: az elsõ : finom(Fagylalt), a második: kaphato(Fagylalt). A finom(Fagylalt) részfeladatot a megfelelõ definíció elsõ PROLOG állításával, a

finom(rum). tényállítással illeszthetjük. A rum konstans, a Fagylalt változó, úgyhogy az illesztés sikerülni fog Ilyen esetekben, amikor egy változó és egy konstans illeszkedik, az illesztés után a változó felveszi a konstans értékét. Így történik a PROLOG-ban az értékátadás, hiszen értékadó utasítás - a hagyományos nyelvekkel ellentétben - nincs. A mintaillesztéssel történõ értékátadások összességét egyesítésnek is szoktuk nevezni. Tehát a Fagylalt változó felveszi a rum értékét. Igen ám, de a Fagylalt változónak további elõfordulásai is vannak a vasarol definíciójában: vasarol(Fagylalt):finom(Fagylalt), kaphato(Fagylalt). szerepel a bal oldalon, továbbá a jobb oldalon mindkét elemi állításban. A változók hatásköre PROLOG-ban egy PROLOG állítás. Ez azt jelenti, hogy ha a program futása során egy változó egyik elõfordulása felvesz egy értéket, akkor az ugyanazon állításon belüli összes többi

elõfordulás is automatikusan felveszi ugyanazt az értéket. Így tehát, amikor vesszük a következõ részfeladatot, az ott szereplõ Fagylalt változó helyett már a rum értéket kell figyelembe vennünk, azaz ezt a részfeladatot kell megoldanunk. kaphato(rum). Ez azonban nem fog sikerülni, mivel a kaphato definíciójában sehol sem szerepel a rum, és konstans csak ugyanazzal a konstanssal illeszkedik. Márpedig ha egy feladat egyik részfeladatát nem sikerül megoldani, akkor a teljes feladat sem oldható meg. Itt le is állna a PROLOG következtetési rendszere, és kiírná, hogy nem sikerült a feladatot megoldani, ha nem rendelkezne a visszalépés rendkívül ügyes tulajdonságával. Visszalépésre akkor kerül sor, ha az egyik részfeladat megoldása nem sikerül, de egy másik, elõzõleg megoldott részfeladatot több módon is meg lehet oldani, és még van ki nem próbált megoldási lehetõség. Ehhez persze az kell, hogy visszalépjünk a feladat

megoldásának folyamatában, állítsuk vissza a programot egy korábbi állapotára, felejtsük el a közben történt értékátadásokat, és próbáljunk ki egy új utat. Például a mi programunkban a finom(Fagylalt) részfeladat megoldására most próbálkozzunk a definíció második tényállításával: finom(tutti frutti). Ekkor a mintaillesztés után a Fagylalt felveszi a tutti frutti értéket, és a kaphato(tutti frutti) részfeladatot is könnyûszerrel meg tudjuk oldani. Ezzel teljesítettük a vasarol definíció mindkét feltételét, így a vasarol(Fagylalt) feladat is megoldott, mégpedig úgy, hogy a Fagylalt felvette a tutti frutti értéket. • Ha most megnézzük, hogy mely részfeladat megoldása hiányzik még az eredeti feladat megoldásából, láthatjuk, hogy a fagylalt definíciójában szerepelnek még az nl és a write elemi állítások, amelyekrõl ez idáig még nem volt szó. Ezek olyan részfeladatokat jelölnek ki, amelyekhez nem találunk

definíciót a FAGYLALT programban. Ezek ugyanis olyan közhasználatú, szinte minden programban használatos eljárások (soremelés és kiírás), hogy ezek definícióját érdemes volt beépíteni magába a PROLOG rendszerbe. Úgy is hívják õket, hogy beépített predikátumok, vagy beépített eljárások. A beépített eljárások köre és formája mindig az aktuális megvalósítástól függ A könyv II FÜGGELÉK-ében felsoroljuk az MPROLOG beépített eljárásait. Tehát a vasarol(Fagylalt) részfeladatot sikerült megoldani. Fagylalt = tutti frutti nl write("Menj a boltba es vegyel ") write(Fagylalt) write(" fagylaltot!") nl • értékkel; hatására soremelés történik; hatására a rendszer kiírja az idézõjelek közötti szöveget; hatására kiírja a rendszer a Fagylalt változó aktuális értékét (most tutti frutti); hatására kiírja az idézõjelek közötti szöveget; hatására soremelés történik És készen is vagyunk. A

rendszer kiírja, hogy Yes, és leáll A feladatot sikerült megoldani 1.8 Különbözõ feladatok • Mi történne ha a fagylalt definíciójából kihagynánk a kiíró utasításokat, azaz a definíció a következõ szabályokból állna: fagylalt:- vasarol(Fagylalt). fagylalt:- otthon marad. A program pontosan úgy mûködne, mint az elõzõ esetben, csak éppen a Yes-en kívül nem írna ki semmit. Azaz csak azt az információt szolgáltatná, hogy tudunk fagylaltot vásárolni. De hogy mit, azt örök homály fedné, elvesztettük volna a lényeges információt. Van még egy módja annak, hogy megtudjuk a keresett fagylalt nevét. Vegyük bele a definíció bal oldalába is a Fagylalt változót a következõképpen: fagylalt(Fagylalt):- vasarol(Fagylalt). fagylalt(Fagylalt):- otthon marad. Ekkor, mielõtt a rendszer leállna, és kiírná, hogy Yes, saját magától kiírja a "Fagylalt" értékét a következõ formában: Fagylalt = tutti frutti. Ez igazán

nagy elõzékenység a PROLOG rendszer részérõl. Mi mégis a kiíró utasítások használata mellett döntöttünk, mivel ily módon sokkal kellemesebb, embernek szóló üzenetet tudtunk megtervezni. • Mi történne akkor, ha egyetlen finom fagylalt se lenne kapható, ha például a tényállítások így módosulnának: finom(rum). finom(eper). finom(malna). kaphato(vanilia). kaphato(csoki). kaphato(tutti frutti). Ekkor bizony a vasarol részfeladatot nem tudnánk megoldani, és így a fagylalt feladat elsõ nekifutására nem sikerülne. Igen ám, de a fagylalt beszerzésének két módja van, s ha az elsõ nem sikerült, a PROLOG rendszer megpróbálkozik a másodikkal. És kiderül, ahogy ha a kedvezõbb megoldás nem is áll a rendelkezésünkre (azaz nem tudunk kedvenc fagylaltból vásárolni), akkor még mindig otthon maradhatunk, és a hûtõszekrénybõl elõvehet)ük azt a fagylaltot, ami van. Ekkor a rendszer a következõket írná ki: Maradj otthon es egyel olyan

fagylaltot, ami van! Yes Van egy remek trükk, amellyel megtudhatjuk a feladat összes lehetséges megoldás, mégpedig igen könnyen. Csapjuk be a PROLOG rendszert, adjunk neki egy olyan célállítást, amely két részfeladatból áll: az elsõ legyen az eredeti feladat, a második pedig egy olyan részfeladat, amelyrõl eleve tudjuk, hogy megoldhatatlan. Például ? fagylalt, fail. • A fail szó kudarcot jelent, és éppen erre a célra van fenntartva. A fail feladat biztos kudarcot és ezért biztos visszalépést generál. Mi is fog itt lejátszódni? Elõször a PROLOG rendszer megoldja a fagylalt részproblémát sikeresen, kiírja, hogy tutti frutti fagylaltot kell vásárolnunk, majd pedig megpróbálja megoldani a fail részfeladatot. Ez nem sikerül neki, és a rendszer úgy érzi, hogy a teljes feladat megoldása is kudarcba fulladt. Ekkor visszalép, és megpróbálja a fagylalt részfeladatot egy másik úton megoldani. Ez megint sikerül, és ki is írja, hogy csoki

fagylaltot kell vásárolni, de utána a fail-el találkozva megint kudarc éri. A PROLOG rendszer kétségbeesetten állandóan visszalép, és új meg új megoldásokat állít elõ (itt összesen négyet), amíg csak lehetséges. Úgy is mondjuk, hogy bejárja a teljes keresési fát, keresi azt a megoldást, amitõl a rákövetkezõ részfeladat esetleg megoldható lesz. Mi persze tudjuk, hogy lehetetlenre vállalkozik, de nekünk pont az volt a célunk, hogy megtudjuk az elsõ részfeladat összes lehetséges megoldását, és ezt meg is kapjuk. Amikor az összes lehetõség kimerült, a rendszer szomorúan konstatálja a végsõ kudarcot, kiírja, hogy NO, és csalódottan leáll. Ezt a trükköt nagyon gyakran alkalmazzuk a PROLOG programozásban. És a rendszer intelligenciáját csak dicsérhetjük, hiszen egy feladat megoldása vagy az összes lehetséges megoldás felkutatása gyakorlatilag ugyanannyi programozási munkát igényel. Aki programozott már más,

hagyományos nyelveken, azonnal láthatja, hogy milyen komoly különbség lenne a két feladat beprogramozásában minden más, visszalépés nélküli, algoritmikus nyelven. 1.9 Gyakorlatok l. Módosítsa úgy a programot, hogy a vasarol definíciója még egy feltételt tartalmazzon Például: A Fagylalt-ot vásároljuk, ha a Fagylalt finom, kapható és olcsó. 2. Mi történne akkor, ha a finom jelenlegi definícióját kicserélnénk a következõ, egy tényállításból álló definícióra? finom(Y). Mit jelent ez a tényállítás? 3. Módosítsa a programot, hogy az otthon maradáshoz feltétel járuljon! Például: Otthon maradunk akkor, ha esik az esõ. 4. Módosítsa úgy a programot, hogy egyáltalán ne tudjunk fagylaltot enni! 2. PROGRAM: CSALÁD module csalad. body. anya(anna, janos). anya(mari, kati). anya(mari, peter). anya(mari, erik). anya(zizi, miki). hazastars(anna, robert). hazastars(mari, janos). hazastars(zizi, peter). apa(APU, GYEREK):hazastars(ANYU, APU),

anya(ANYU, GYEREK). szulo(SZULO, GYEREK):- anya(SZULO, GYEREK). szulo(SZULO, GYEREK):- apa(SZULO, GYEREK). nagyszulo(NAGYI; UNOKA):- szulo(NAGYI, SZULO), szulo(SZULO, UNOKA). utod(X, OS):- apa(OS, X). utod(X, OS):- apa(VALAKI, X), utod(VALAKI, OS). endmod/* csalad / Programfutások: ? apa(peter, miki). Yes Péter-e Miki apja? ? apa(robert, mari). NO Róbert-e Mari apja? ? apa(X, peter). X = janos Ki Péter apja? Continue (y/n) ? y NO ? apa(janos, GYEREK). GYEREK = kati Continue (y/n) ? y GYEREK = peter Continue (y/n) ? y GYEREK = erik Continue (y/n) ? y NO Kik Janos gyermekei? ? nagyszulo(X, miki). X = mari Continue (y/n) ? y X = janos Continue (y/n) ? n OK Kik Miki nagyszülõi? ? utod(miki, robert). Yes Miki utódja-e Róbertnek? ? utod(miki, A). A = peter Continue (y/n) ? y A = janos Continue (y/n) ? y A = robert Continue (y/n) ? y NO Kik Miki õsei? ? hazastars(X, Y). X = anna Y = robert Continue (y/n) ? y X = mari Y = janos Continue (y/n) ? y X = zizi Y = peter

Continue (y/n) ? y NO Ki kinek a házastársa? 2.l A program célja, feladata • A CSALÁD program a családon belüli kapcsolatokat rögzíti, illetve definiálja. A program segítségével meg lehet tudni, hogy egy adott családon belül ki kinek az apja, anyja, házastársa, gyermeke, nagyszülõje, unokája és apai ági leszármazottja. 2.2 A feladat feltételrendszere • Egy konkrét családra vonatkozóan felsoroltuk az anya-gyermeke és a feleség-férj kapcsolatokat. Minden más kapcsolatot ezek segítségével definiáltunk. Valaki akkor apja egy gyermeknek, ha van egy házastársa, aki anyja ennek a gyermeknek. Valaki akkor szülõje gyermeknek, ha vagy anyja, vagy apja a gyermeknek. Valaki nagyszülõje egy gyermeknek, ha szülõje valakinek, aki szülõje a gyermeknek. Az apai ágon való leszármazott-õs kapcsolat a legnehezebb. Valaki leszármazottja egy személynek akkor, ha vagy az illetõ személy az apja, vagy az apja leszármazottja az illetõ személynek. 2.3

Bemenõ, kimenõ paraméterek • A apa reláció a CSALÁD programban két argumentumú predikátum. Nézzük meg, hogy hányfajta kérdést fejezhet ki a apa célállítás? (* 1 ) ?. apa(peter, miki) célállítás mindkét argumentuma konstans. Itt a feladat az, hogy megállapítsuk, igaz-e, hogy az apa reláció fennáll Péter és Miki között, azaz igaz-e, hogy Péter Miki apja. Az apa definíciója apa(APU, GYEREK):- hazastars(ANYU, APU), anya(ANYU, GYEREK). alapján a PROLOG rendszer a következõképpen fog mûködni: az APU változó illeszkedik a peter konstanssal, a GYEREK változó a miki konstanssal, APU = peter, GYEREK = miki, az elsõ részfeladat meghívásakor, hazastars(ANYU, peter), mivel van olyan tényállításunk, ahol a második argumentum peter, hazastars(zizi, peter). az illesztés sikeres, és az ANYU változó felveszi a zizi konstans értékét. Így a következõ, az anya részfeladatot már a ANYU = zizi, GYEREK = miki értékekkel hívja meg a

PROLOG futtató rendszere. És mivel van anya(zizi, miki). tényállításuik, a rendszer úgy érzi, hogy a feladatot sikeresen megoldotta, kiirja azt, hogy Yes és leáll. Az alábbi (* 2 ) ? apa(robert, mari). célállítás ugyanolyan kérdést tesz fel, mint az elsõ, de itt a válasz negativ lesz, azaz Róbert nem apja Marinak. Miért? Az APU = robert és GYEREK = mari értékátadások után a hazastars(ANYU, robert) részfeladatot sikerül megoldani ANYU = anna értékkel, de az anya(anna, mari) részfeladat nem illeszkedik egyik tényállítassal sem, így a rendszer azt írja ki, hogy NO és leáll. A következõ (* 3 ) ? apa(X, peter). célállítás egyik argumentumában változó szerepel, a másikban konstans. Ha változó szerepel egy célállításban, akkor a feladat egészen más jellegû, mint az (* 1 ) és a ( 2 ) esetekben. Ilyenkor az a feladat, hogy keressük meg azt az értéket (vagy azokat az értékeket), amely(ek) mellett a célállítás predikatuma igaz

lesz. Esetünkben például egy olyan személyt keresünk, akire vonatkozóan az apa relició igaz lesz akkor, ha a második argumentum peter. Magyarul, a feladat a következõ kérdést jelenti: Ki Péter apja?. Az ilyen típusú kérdéseknél a legtöbb PROLOG rendszer, és így az IBM PC MPROLOG is kiiírja, hogy a feladatot milyen értékkel sikerült megoldani. Ezen kívül a rendszer még további szolgáltatást is nyújt: megkérdezi a felhasználót, akarja-e folytatni a futtatást úgy, hogy újabb megoldást keres a feladatra? ? apa(X, peter). X = janos Continue (y/n) ? Ha nem akajuk folytatni, akkor leütjük az n billentyût, és a rendszer ezt OK üzenettel veszi tudomásul. Ha akarjuk folytatni, akkor az y begépelése és elküldése után a PROLOG futtató renszere visszalép, és megpróbál új megoldást találni. Ha már nincs több megoldás, akkor NO üzenettel leáll Azért NO-val, és nem Yes-szel áll le, mert a PROLOG rendszerbe ugyanaz az elv van

beépítve, amelyet a fagylalt, fail. célállításnál láthattunk, azaz meghiúsult kisérlet, után lép vissza. Így hamissal kell leállnia • Most vegyünk egy olyan célállítást, amelyben az apa elsõ argumentuma konstans, és a második változó: (* 4 ) ? apa(janos, GYEREK). Milyen kérdést fejez ki ez az állítás? Nyilvánvalóan azt, hogy Ki János gyermeke? vagy Kik János gyeremekei?, attól függõen, hogy hány van neki. Ez azt jelenti, hogy az apa definícióval egyszerre definiáltuk azt, hogy ki kinek az apja, és azt, hogy ki kinek a gyermeke. A kérdés csak az, hogy melyik argumentum adott, azaz melyik argumentumot tekinthetjük bemenõ paraméternek, és melyik argumentum változó, amelyhez értéket keresünk, azaz melyik argumentum játssza a kimenõ paramétert? • Nézzük most meg azt, hogy mit fejez ki egy olyan célállítás, amelyben csak változók szerepelnek, azaz Bemenõ paraméterünk nincs, csak kimenõ paraméterünk van: (* 5 ) ? apa(X,

Y). Ez a célállítás azt jelenti, hógy keresünk egy apa-gyermek kapcsolatot a családban, és a PROLOG rendszer azt az apagyermek kapcsolatot fogja kiírni, amit elsõként megtalál. Ez történetesen az X = robert, Y = janos lesz. • A bemenõ-kimenõ paraméterek rugalmas kezelése nagyon kényelmessé teszi a PROLOG programozást, hiszen egy adott program sokféle feladat megoldására lehet alkalmas. Ez az egyik oka annak, hogy a PROLOG programok olyan tömörek. 2.4 Különbözõ típusú definíciók • Ahogy azt már az elõzõekben mondottuk, egy PROLOG program definíciók sorozata. Most egy kicsit részletesebben megvizsgáljuk a definíciók szerkezetét, típusait a feladatmegoldás szempontjából, mivel programírásnál a definíciók az önállóan kezelhetõ értelmes egységek, és nem a PROLOG állítások. A definíciók sorrendje például, egy PROLOG programban tetszõleges, de a PROLOG állítások sorrendje egy definíción belül már igen fontos lehet.

Emlékezzünk csak vissza: a következtetési mechanizmus egy definíción belül mindig az elsõ PROLOG állítást választja elõször a mintaillesztéshez, és csak ha nem sikerült illeszteni, akkor tér rá a második változatra. Tehát a sorrend fontos • A CSALÁD program hat definíciót tartalmaz. Vegyük õket sorra A anya definíciója négy tényállításból áll. Az anya predikátum kétargumentumú, az elsõ argumentumban az anya, a másodikban a gyermek neve áll. Tulajdonképpen egyszerûen fel vannak sorolva a család anya-gyermek kapcsolatai A definícióban sehol sincs változó, mindenütt.konstansok szerepelnek Ez az egész olyan, mintha felsoroltuk volna az adatokat. Ezért is hívjuk a csak tényállításból álló definíciókat adatbázis definícióknak Az elnevezés azt sugallja, hogy ilyen típusú definíciókkal adatbázisokat lehet megadni, és ez valóban így is van. Egy PROLOG program adatbázisa sok rokon vonást mutat a relációs

adatbázisokkal. A hazastars definíciója is adatbázis definíció, predikátuma szintén két argumentumú. A apa definíciója csak egy PROLOG szabályból áll, amely két feltételt tartalmaz: apa(APU, GYEREK):- hazastars(ANYU, APU), anya(ANYU, GYEREK). Ezt a szabályt így is lehet értelmezni: az apa(APU, GYEREK) feladatot csak úgy tudjuk megoldani, ha megoldjuk a hazastars(ANYU, APU) részfeladatot, és utána megoldjuk az anya(ANYU, GYEREK) részfeladatot. Lényeges az, hogy itt a két részfeladatot logikái ÉS kapcsolja össze. 1. ábra Részfeladatok közötti ÉS • Az olyan egy szabályból álló definíciókat, amelyekben a szabály jobb oldala több elemi állítást tartalmaz, ÉS definícióknak hívjuk. Nézzük meg most a szulo definícióját: szulo(SZULO, GYEREK):- anya(SZULO, GYEREK). szulo(SZULO, GYEREK):- apa(SZULO, GYEREK). Ez a definíció két szabályból áll. A szabályok bal oldala megegyezik, a jobb oldaluk egy-egy feltételbõl áll Ezt a

definíciót így is lehet értelmezni: a szulo(SZULO, GYEREK) feladatot csak úgy tudjuk megoldani, ha vagy az anya(SZULO, GYEREK), vagy az apa(SZULO, GYEREK) részfeladatot oldjuk meg. Lényeges, hogy itt a két részfeladatot logikai VAGY kapcsolja össze. 2.ábra Részfeladatok közötti VAGY • Az egynél több szabályból álló definíciókat VAGY definícióknak nevezzük. • Természetesen vannak olyan PROLOG definíciók, amelyek több szabályból állnak, és a szabályok jobb oldala is több elemi állítást tartalmaz. Az ilyen ÉS vagy VAGY definíció? Tulajdonképpen a kettõ kombinációja, és az ilyen definíció alapján a részfeladatok ún. ÉS-VAGY fáját lehet felrajzolni Egy szabályon belül a részfeladatok között logikai ÉS kapcsolat van, a különbözõ szabályok közötti részfeladatok között logikai VAGY kapcsolat van. Soroljuk az ilyen definíciókat a VAGY definíciók osztályába, mert a két logikai operáció közül itt a VAGY a

jellemzõbb. A nagyszulo definíció - hasonlóan az apa definíciójához - csak egy szabályból áll, ezért ÉS definíció. Az "utod" definíciója két szabályból áll, de mégsem egyszerû VAGY definíció. Vegyük észre, hogy az utódnak lenni fogalmat saját magával definiáltuk! utod(X, OS):- apa(OS, X). utod(X, OS):- apa(VALAKI, X), utod(VALAKI, OS). • Az ilyen definíciókat rekurzív definícióknak hívjuk. Vizsgáljuk meg a fenti definíciót részletesen A definíció két szabályból áll, tehát utódnak lenni kétfajta módon lehet: vagy úgy, hogy az õsöm az apám, vagy úgy, hogy az apám egy olyan személy, aki egy õsöm utódja. Azaz az utod feladatot visszavezettük az apa részfeladatra és saját magára, az utód részfeladatra. Ezt így lehetne ábrázolni: 3. ábra Rekurzív definíció Az ábrán látható hurok minden baj forrása, hiszen rekurzív definíciókkal "kiválóan" lehet végtelen ciklusokat elóállítani.

• Mire kell vigyázni a REKURZÍV definícióknál? Elsõsorban arra, hogy a rekurzív definíció alapján mûködõ feladatmegoldó algoritmusnak ne csak egy önmagába visszatérõ ága legyen, hanem legyen egy ún. leállító ága is Ez a legfontosabb Ezért, amikor PROLOG-ban leírunk egy rekurzív definíciót, sajnos tudnunk kell, hogy hogyan fog a PROLOG feladatmegoldó mechanizmusa mûködni, mikor melyik részfeladat megoldására kerül sor, különben könnyen végtelen ciklusba eshet a program. A következõ fejezetben bemutatjuk, hogy hogyan oldunk meg egy feladatot rekurzióval. 2.5 Rekurzivitás • Tekintsük a következõ kérdést: Igaz-e az, hogy Miki utódja Róbertnek? Erre úgy kaphatunk választ, hogy a kérdést megfogalmazzuk egy célállítás formájában, majd ezt futtatjuk a PROLOG rendszerrel: (* 1 ) utod(miki, robert). A feladat megoldásához rendelkezésünkre áll az utod definíciója: utod(X, OS):- apa(OS, X). utod(X, OS):- apa(VALAKI, X),

utod(VALAKI, OS). A program futása úgy kezdõdik, hogy a célállítást illesztjük az elsõ szabály bal oldalával. Illesztés után X = miki, OS = robert értékekkel az apa(robert, miki) részfeladat megoldására kerül sor. Ha igaz lenne az, hogy Róbert Miki apja, akkor a program máris leállna Yes-szel, és nekünk nem kellene belebonyolódnunk a rekurzív hívás rejtelmeibe. De ez sajnos nem igaz, az apa(robert, miki) részfeladatot nem sikerül megoldani, ezért a rendszer visszalép, és most az utod definíciójának második szabályával próbálkozik. A második szabály két részfeladatra bontja le a feladatot, az elsõ megint az apa, a második viszont maga az utod részfeladat. Nézzük meg, hogy miben külbnbözik a két apa feltétel a két különbözõ szabályban? Azonnal láthatjuk, hogy az elsõ argumentumukban. Az elsõ szabályban az illesztés utáni értékadások miatt az apa két konstans értékkel lett meghívva, míg a második szabályban a

VALAKI változó nem szerepel a szabály jobb oldalán, és ezért érték nélkül marad. Azaz az illesztés, az X = miki, OS = robert értékátadások után meghívjuk az apa(VALAKI, miki) részfeladatot, ami azt a kérdést jelenti, hogy Ki Miki apja?. Ezt a részfeladatot sikerül megoldanunk VALAKI = peter értékkel, és így a következõ részfeladatunk a (* 2 ) utod(peter, robert) lesz. Tehát az eredeti feladatunkat, hogy Miki utódja-e Róbertnek, visszavezettük arra a kérdésre, hogy Péter utódja-e Róbertnek. Úgy is mondhatjuk, hogy Miki akkor utódja Róbertnak, ha Péter Miki apja, és Péter utódja Róbertnak. • Vegyük észre, hogy egy lépcsõfokkal közelebb kerültünk a megoldáshoz, léptünk egyet a családfában, amely az utódtól az õsig visz. Az eredeti utód feladatot (* 1 )-gyel jelöltük, az új feladatot ( 2 )-vel. A (* 2 ) feladatot pont úgy próbálja a PROLOG rendszer megoldani, mint az (* 1 ) esetnél. Veszi az utod definíciójának elsõ

szabályát, majd amikor kiderül, hogy Róbert nem apja Péternek, tehát az elsõ szabály alapján újra kudarcot vall, veszi a második szabályt. Az illesztés után az X = peter, OS = robert értékátadások miatt az apa(VALAKI, peter) részfeladatra kerül sor, amelyet VALAKI = janos értékkel sikerül megoldani, így a következõ részfeladat a (* 3 ) utod(janos, robert) lesz. Tehát visszavezettük a feladatunkat arra, hogy • János utódja-e Róbertnak? És most érkeztik el arra a pontra, hogy az új, (* 3 ) utod probléma megoldásához a definíció elsõ szabályát fogjuk használni, ugyanis az apa(robert, janos) részfeladatot sikerül megoldani. Ezáltal megoldottuk a (* 3 ) részfeladatot, amely a ( 2 ) részfeladat feltétele volt. Ez viszont az eredeti (* 1 ) feladat feltétele volt. Tehát az (* 1 ) utod(miki, robert) feladatot megoldottuk, azaz igazoltuk, hogy Miki utódja Róbertnek. A rendszer kiírja azt, hogy Yes és leáll • Próbáljuk meg

ábrázolni a feladatok és részfeladatok sorát úgy, hogy az egymásba ágyazás kapcsolati rendszerét is látni lehessen: minél mélyebben van beskatulyázva egy részfeladat, annál nagyobb bekezdéssel fogjuk írni. Feladat (* 1 ) (* 1 ) (* 2 ) (* 2 ) (* 3 ) Mi történik? utod(miki, robert) apa(robert, miki) utod(miki, robert) apa(VALAKI, miki) utod(peter, robert) apa(robert, peter) utod(peter, robert) apa(VALAKI, peter) utod(janos, robert) apa(robert, janos) 1. szabály, illesztés; kudarc, visszalépés; 2. szabály, illesztés; sikerül, VALAKI = peter; 1. szabály, illesztés; kudarc, visszalépés; 2. szabály, illesztés; sikerül, VALAKI = janos; 1. szabály, illesztés; sikerül; (* 3 ) sikerül; (* 2 ) sikerül; (* 1 ) sikerül; A feladatot megoldottuk. Yes • Az összegzésbõl is láthatjuk, hogy az utod rekurzív definíció két szabálya közül az elsõ játszotta a leállító szerepét, a második pedig az újrahívás szerepét. Mivel a PROLOG rendszer

mindig az elsõ szabályt hívja meg elõször, az algoritmus úgy festett, hogy elõször mindig ellenõriztük, hogy készen vagyunk-e, és ha nem, akkor folytattuk a rekurzív hívást. Ez a legáttekinthetõbb taktika a végtelen ciklus kiküszöbölésére. De nem mindig ez a leghatékonyabb A könyv 5 es 9 fejezetében folytatni fogjuk a rekurzív definíciók és eljárások tanulmányozását, és szó lesz különbözõ stratégiákról is. 2.6 Gyakorlatok 1. Definiálja a nagymama relációt! 2. Bõvítse az adatbázist a testvér relációval: testver(kati, peter). testver(kati, erik). /* Kati testvere Peternek / /* Kati testvere Eriknek / testver(peter, erik). /* Petér testvere Eriknek / • Elég ennyi tényállítás? Mi lesz a válasz arra a kérdésre, hogy Erik testvére-e Péternek? Azaz milyen választ ad a PROLOG rendszer a következõ feladatra? ? testver(erik, peter) 3. Elég-e e a testver definíciójához az elõzõ, 2 gyakorlatban szereplõ három

tényállítás, ha a következõ fiver vagy nover definíciót csatoljuk a programhoz? fiver vagy nover(X, Y):- testver(X, Y) fiver vagy nover(X, Y):- testver(Y, X) Mi lesz a válasz a ? fiver vagy nover(erik, peter). feladatra? Mit fejez ki a fiver vagy nover definíciója? 4. Igaz-e az, hogy ? utod(robert, robert). Miért? 3. PROGRAM: NAGY CSALÁD module nagy csalad. body. ferfi(robert). ferfi(janos). ferfi(peter). ferfi(erik). ferfi(miki). ferfi(pali). ferfi(tamas). ferfi(sandor). asszony(anna). asszony(mari). asszony(kati). asszony(zizi). anya(anna, janos). anya(mari, kati). anya(mari, peter). anya(mari, erik). anya(zizi, miki). anyá(kati, tamas). anya(kati, sandor) hazastars(anna, robert). hazastars(mari, janos). hazastars(zizi, peter). hazastars(kati, pali). apa(APU, GYEREK):hazastars(ANYU, APU), anya(ANYU, GYEREK). szulo(SZULO, GYEREK):- anya(SZULO, GYEREK). szulo(SZULO, GYEREK):- apa(SZULO, GYEREK). nagyszulo(NAGYI, UNOKA):- szulo(NAGYI, SZULO), szulo(SZULO, UNOKA).

nover(NOVER, VALAKI):asszony(NOVER), szulo(SZULO, NOVER), szulo(SZULO, VALAKI), not(equal(NOVER, VALAKI)). fiu testvér(BATY, VALAKI):ferfi(BATY), szulo(SZULO, BATY), szulo(SZULO, VALAKI), not(equal(BATY, VALAKI)). unoka testver(GYEREK1, GYEREK2):szulo(SZ1, GYEREK1), szulo(SZ2, GYEREK2),(fiu (SZ1, SZ2) ; nover(SZ1, SZ2)). equal(X,X). /* not(X):- X, !, fail. */ /* not(X). */ endmod/* nagy csalad /. Programfutások: ? fiu testver(X, kati). X = peter Continue (y/n) ? y X = peter Continue (y/n) ? y X = erik Continue (y/n) ? y X = erik Continue (y/n) ? y NO ? nover(X, erik). X = kati Continue (y/n) ? y X = kati Continue (y/n) ? y NO ? unoka testver(tamas,X). X = miki Continue (y/n) ? y X = miki Continue (y/n) ? y NO ? nagyszulo(Y, miki). Y = mari Continue (y/n) ? y Y = janos Continue (y/n) ? y NO 3.1 A program célja, feladata • A NAGY CSALÁD program tulajdonképpen a CSALÁD program folytatása, bõvítése. A program segítségével újabb családi relációkra lehet rákérdezni,

nevezetesen a nõvérekkel, bátyákkal, unokatestvérekkel kapcsolatban lehet kérdéseket feltenni. 3.2 A feladat feltételrendszere • A család az elõzõ programhoz képest bõvült. Kati férjhez ment Palihoz, és született két gyermekük, Tamás és Sándor. A program adatbázisa ennek megfelelõen módosult • Az adatbázist két új definícióval is kiegészítettük: a ferfi és az asszony definíciókkal rögzítjük, hogy ki milyen nemû a családban. Valaki nõvére egy személynek, ha asszony, és van közös szülõjük. Valaki bátyja egy személynek, ha férfi, és van közös szülõjük. Valaki unokatestvére egy másik személynek, ha az egyik szülõje bátyja vagy nõvére a másik valamelyik szülõjének. 3.3 Ismétlõdés és ennek elkerülése • Nézzük azt a kérdést, hogy Kik Kati bátyjai?, azaz futtassuk le a következõ feladatot: ? fiu testver(X, kati). Az eredmény igen érdekes lesz. A PROLOG rendszer kétszer fogja kiadni Péter és

kétszer Erik nevét Mintha Katinak négy bátyja lenne, pedig csak kettõ van. Miért? Nézzük meg fgyelmesen a fiu testver definícióját: fiu testver(BATY, VALAKI):ferfi(BATY), szulo(SZULO, BATY), szulo(SZULO, VALAKI), not(equal(BATY, VALAKI)). Vegyük sorra a jobb oldalon levõ feltételeket: a ferfi(BATY) mindössze azt rögzíti, hogy egy bátynak férfinak kell lennie, ez rendben is van, ez nem okozhat bajt. De az utána következõ két feltétel igen A következõ feltétel a szulo(SZULO, BATY), szulo(SZULO, kati) azt jelenti, hogy a báty szülõje és Kati szülõje ugyanaz a személy kell hogy legyen, mivel mindkét részfeladat elsõ argumentuma ugyanaz a SZULO változó. Ha az egyik értéket kap, a másik is automatikusan felveszi ugyanazt az értéket Emlékezzünk a szülõ definíciójára: szulo(SZULO, GYEREK):- anya(SZULO, GYEREK). szulo(SZULO, GYEREK):- apa(SZULO, GYEREK). Tehát egy személy akkor szülõje egy gyermeknek, ha vagy az anyja, vagy az apja a

gyermeknek. Katinak és Péternek két közös szülõje van; az anyjuk is és az apjuk is közös. Így tehát ez a két részfeladat a szulo(mari, peter), szulo(mari, kati) szulo(janos, peter), szulo(janos, kati) értékekkel kétféleképpen is teljesülhet. A rendszer csak azt érzékeli, hogy a feladat megoldó mechanizmusa két különbözõ ágat járt be, és ezért büszkén kétszer kinyomtatja Pétert mint Kati bátyját. Ez az ismétlõdés oka A fiu testver definíció jobb oldalának utolsó feltétele not(equal(BATY, VALAKI)) azt rögzíti, hogy valaki nem lehet saját magának a bátyja. Erre azért van szükség, mert ha ezt elhagynánk, a rendszer kiadná egy személy bátyjai közt saját magát is, feltéve természetesen, hogy az illetõ férfi. Például a ? fiu testver(X, erik). célállítás futtatása négy eredményt is hozna: X = peter Continue (y/n) ? y X = peter Continue (y/n) ? y X = erik Continue (y/n) ? y X = erik Continue (y/n) ? y NO de ebbõl a

négybõl csak egy lenne igazi eredmény. • A not és equal·definíciókkal a következõ fejezetben fogunk foglalkozni. Most térjünk vissza a duplikáció okára. Az ilyen típusú feladatoknál, amikor valaminek az összes lehetséges megoldását keressük, gyakran találkozhatunk hasonló problémával. A hibát az okozta, hogy a fiu testvér definíciójához egy kicsit más szülõfogalomra volna szükség. Kellene egy olyan szülõfogalom, amely azt fejezi ki, hogy csak az egyik szülõ. Az apa vagy az anya, de egymást kizárva Ilyen definíciót könnyûszerrel tudunk szerkeszteni a cut segítségével, amelyet ebben a PROLOG megvalósításban a felkiáltójel (!) karakter jelöl. A cut magyarul vágást jelent, és ez egész jól kifejezi azt a tevékenységet, amelyet a PROLOG rendszer végrehajt a cut szimbólum hatására: levág a definícióból bizonyos lehetõségeket. Nézzük meg, hogyan módosulna a.szülõ definíciója a cut szimbólummal: szulo1(SZULO,

GYEREK):- anya(SZULO, GYEREK), !. szulo1(SZULO, GYEREK):- apa(SZULO, GYEREK). Ezt az új definíciót szulo1-nek neveztük el, hogy megkülönböztessük az eredetitõl, mivel mindkettõre szükség lesz. • Hogyan mûködik a cut? Röviden: amikor a program simán fut (elõre), akkor nincs semmilyen látható hatása, de feljegyzi a rendszer, hogy ott van. Azonban amikor a feladatmegoldó algoritmus visszalép, akkor a cut letiltja a ! szimbólumtól balra lévõ részfeladatokban és magában a definícióban is meglévõ összes egyéb, még fel nem derített alternatívát. Például ha a szulo1 feladat megoldása során az anya részfeladatot sikerül megoldani, akkor egy késõbbi visszalépés esetén sem kerül sor a szulo1 második állításának, azaz a apa-nak a használatára. A második állítással való illesztést csak abban az esetben végzi el a rendszer, ha az elsõ állításban levõ anya feltétel nem teljesül, és a kudarc visszalépésre kényszeríti a

rendszert, még mielõtt a ! szimbólumot észlelte volna. • Írjuk át úgy a programot, hogy a fiu testver és a nover definíciókban használjuk a szulo1-et, de a nagyszulo és az unoka testver definíciókat ne változtassuk: module nagy csalad. body. ferfi(robert). ferfi(janos). ferfi(peter). ferfi(erik). ferfi(miki). ferfi(pali). ferfi(tamas). ferfi(sandor). asszony(anna). asszony(mari). asszony(kati). asszony(zizi). anya(anna, janos). anya(mari, kati). anya(mari, peter). anya(mari, erik). anya(zizi, miki). anya(kati, tamas). anya(kati, sandor). hazastars(anna, robert). hazastars(mari, janos). hazastars(zizi, peter). hazastars(kati, pali). apa(APU, GYEREK):hazastars(ANYU, APU), anya(ANYU, GYEREK). szulo(SZULO, GYEREK):- anya(SZULO, GYEREK). szulo(SZULO, GYEREK):- apa(SZULO, GYEREK). szulo1(SZULO, GYEREK):- anya(SZULO, GYEREK), !. szulo1(SZULO, GYEREK):- apa(SZULO, GYEREK). nagyszulo(NAGYI,UNOKA):- szulo(NAGYI, SZULO), szulo(SZULO, UNOKA). nover(NOVER,

VALAKI):asszony(NOVER), szulo1(SZULO, NOVER), szulo1(SZULO, VALAKI), not(equal(NOVER, VALAKI)). fiu testver(BATY, VALAKI):ferfi(BATY), szulo1(SZULO, BATY), szulo1(SZULO, VALAKI), not(equal(BATY, VALAKI)). unoka testver(GYEREK1, GYEREK2):szulo(SZ1, GYEREK1), szulo(SZ2, GYEREK2), (fiu testver(SZ1, SZ2) ; nover(SZ1, SZ2)). equal(X, X). /* not(X):- X, !, fail. */ /* not(X). */ endmod/* nagy csalad /. Programfutások: ? fiu testver(X, kati). X = peter Continue (y/n) ? y X = erik Continue (y/n) ? y NO ? nover(X, erik). X = kati Continue (y/n) ? y NO ? unoka testver(tamas, X). x = miki Continue (y/n) ? y NO ? nagyszulo(Y, miki). Y = mari Continue (y/n) ? y Y = janos Continue (y/n) ? y NO A fenti programfutások eredményeibõl is megbizonyosodhatunk arról, hogy az ismétlõdéseket sikerült kiszûrni. • A cut használatával legyünk nagyon óvatosak! Nagyon vigyázzunk, hogy csak akkor tegyük ki, amikor jól átgondoltuk, hogy semmilyen olyan ágat nem vágunk le vele, amely szükséges

lehet az esetleges visszalépéseknél. Ha például az unoka testver vagy a nagyszülõ definíciójában a szulo1-et, azaz a cut-tal megtûzdelt szülõ definíciót használnánk, teljesen hamis eredményt kapnánk. A következõ ? unoka testver(tamas, X). Feladatot egyáltalán nem sikerülne megoldani, a válasz NO volna, és a ? nagyszulo(Y, miki). feladatra csak Miki nagymamáját kapnánk eredményül, (X = mari), a nagypapa (X = janos) elmaradna. • A cut szimbólumnak rendkívül fontos szerepe van a PROLOG-beli tagadás, a not definíciójában, ezt a következõ fejezetben részletesen kifejtjük. De fogunk még találkozni vele olyan programozási helyzetekben is, ahol a végtelen ciklus valamilyen formáját akarjuk használatával elkerülni (5. és 11 programok) Minden esetben, amikor a cut szimbólumot használni fogjuk, megindokoljuk az okát. 3.4 Tagadás a PROLOG-ban • A fiu testver definíciójának utolsó feltételeként találkozhatunk elõször a not,

azaz a tagadás alkalmazásával: not(equal(BATY, VALAKI)). Azonnal láthatjuk, hogy itt valami új van, mivel dupla zárójelezés az elemi állításokban ezidáig nem szerepelt. Nézzük meg elõször a külsõ zárójel tartalmát: equal(BATY,VALAKI). Ez egy teljen normális elemi állítás. Ha mint részfeladatot fogjuk fel egy szabályban, akkor vagy sikerül megoldani, vagy nem, avagy "tudományosan" fogalmazva: vagy bizonyítható, vagy nem. • Az equal (magyarul: egyenlõ) definíciója: equal(X, X). Tehat az egyenlõnek lenni fogalmat egyetlen tényállítással definiáltuk, amely tulajdonképpen azt jelenti, hogy két dolog egyenlõ, ha illeszthetõk egymással a PROLOG mintaillesztési szabályai szerint. Például equal(X, kati) equal(tamas, tamas) equal(X, BATY) equal(kati, tamas) bizonyítható, bizonyítható, bizonyítható, nem bizonyítható. Következésképpen az equal(BATY, VALAKI) vagy bizonyítható, vagy nem attól függõen, hogy a két

argumentumban éppen milyen értékek vannak, azaz a feladat megoldása során a BÁTY és a VALAKI változók milyen értékeket vettek fel. • Semmi meglepõ sincs abban, hogy a tagadással, azaz a not használatával azt akarjuk elérni, hogy ami idáig bizonyítható volt, az a tagadás hatására ne legyen bizonyítható, és ami nem bizonyítható, az a tagadás hatására bizonyítható legyen. Ez a not funkciója, ezt kell elérni valamilyen ügyes programozási trükkel A not definícióját szinte minden PROLOG rendszer felvette a beépített e1járások sorába, hiszen nélküle nagyon nehéz lenne programozni. A mi MPROLOG rendszerünk is beépített eljárásként kezeli, tehát nem kell külön definiálnunk Ezért adtuk meg a not definícióját magyarázatként, azaz ezért tettük /* és / közé. A /* és / közé esõ programrészt a rendszer elolvassa, de futtatáskor nem veszi figyelembe. A beépített not eljárást kétfajta módon is meg lehet hívni: vagy

úgy, mint a NAGY CSALÁD programban van not(equal(BATY, VALAKI)) két zárójel segítségével, vagy not(equal(BATY, VALAKI) formában. Nézzük meg a definíciót, mert igen tanulságos, és aki egyszer a not definícióját megértette, az nagyot lépett elõre a PROLOG lelkivilágának a megismerésében. not(X):- X, !, fail. not(X). Elsõ pillanatra meghökkentõ, hogy a definíció elsõ szabályának jobb oldalán egy elemi állítás helyett egy változó szerepel. Ez azt jelenti, hogy a változó helyébe egy teljes elemi állítás helyettesíthetõ Ez idáig nem fordult elõ, és ha a PROLOG az elsõrendû matematikai logika formai elõírásait szigorúan betartaná, akkor elõ sem fordulhatna! Ott ugyanis egy változóval csak kifejezéseket lehet illeszteni vagy helyettesíteni, de atomi formulákat (azaz elemi állításokat) semmi esetre sem [13]. Itt viszont a not, azaz a tagadás argumentuma egy elemi állítás, amilyen például az equal(BATY, VALAKI) volt. •

Nézzük meg, hogy mi történik, ha a következõ feladatot szeretnénk megoldani: ? not(equal(peter, zizi)). Ez a feladat a következõ kérdést jelenti: Igaz-e, hogy Péter nem azonos Zizivel? Jó lenné, ha a rendszer azt válaszolná, hogy igen, ami a valóságnak többnyire megfelel. A feladat megoldása úgy kezdõdik, mint mindig: a rendszer megkeresi a not definícióját, és az elsõ szabály baI oldalát, not(X) illeszteni próbálja a célállítással: not(equal(peter, zizi)). Az illesztés sikerül, és ennek hatására az X változó felveszi az equal(peter, zizi) értéket, tehát: X = equal(peter, zizi). Az elsõ szabályban máshol is szerepel az X változó: not(X):- X, !, fail. ezért a második elõfordulás is felveszi ezt az értéket, és így az elsõ részfeladat az equal(peter, zizi) lesz. Ez azonban nem bizonyítható, úgyhogy a feladatot az elsõ szabály segítségével nem sikerült megoldani A rendszer visszalép és a második szabályt alkalmazza,

amely csupán a not(X). feltétel nélküli tényállítás. Az illesztés természetesen sikerül, tehát a feladatot megoldottuk, a rendszer kiírja, hogy Yes és leáll. Ez idáig megfelelt a várakozásunknak • S vajon mi történik, ha a következõ célállítást adjuk meg feladátként: ? not(equal(peter, peter)). Igaz-e, hogy Péter nem azonos Péterrel? Erre természetesen tagadó válászt várunk. Ezt meg is kapjuk a következõképpen: célállítás: definíció: not(equal(peter, peter)). not(X):- X, !, fail. not(X). A rendszer illeszti a célállítást az elsõ szabály bal oldalával, az X = equal(peter, peter) értékadás következtében az equal(peter, peter) részfeladatot próbálja megoldani. Ez sikerül, a következõ lépés a cut szimbólum regisztrálása, majd pedig a fail részfeladat megoldása kudarcba fullad. A rendszer vissza is lépne, ha a cut nem tiltotta volna le! Így viszont nem teheti, és a "fail" kudarca miatt kiírja, hogy

NO és leáll. • Az történt tehát, hogy az elsõ esetben, amikor az equal(peter, zizi) not(equal(peter, zizi)) equal(peter, peter) not(equal(peter, peter)) nem volt bizonyítható, a bizonyítható lett, míg az bizonyítható volt, és a nem bizonyítható, éppen ezt vártuk a negációtól. • Tagadni természetesen akármelyik definiált vagy beépített predikátumot lehet. • A not kicsit furcsa viselkedésével kapcsolatban vizsgáljuk meg a következõ két feladatot. /* 1 / ? asszony(X). /* 2 / ? not(not(asszony(X))). Hány különbözõ megoldása van az 1. és a 2 feladatnak? Igaz-e a PROLOG-ban az, hogy a tagadás tagadása állítás? Megoldás: 1. Az asszony(X) célállításnak négy megoldása lesz: X = anna X = mari X = kati X = zizi 2. A not(not(asszony(X))) feladatra furcsa választ fogunk kapni A kétszeres tagadás ugyan állítás a PROLOG-ban is, de a not visszalépéses megoldása miatt azokat a konstans értékeket, amelyeket az asszony(X)

illesztése után kaptunk, el fogjuk veszíteni. Így a not(not(asszony(X))) ugyan igaz, de az X változó helyére csak egy másik változó kerül (Pl: X = 645). • Ebbõl is látható, hogy a PROLOG-beli tagadás nem teljesen azonos a logikai tagadással. 3.5 Részfeladatok közötti VAGY egy állításon belül • Nézzük meg az unoka testver definícióját: unoka testver(GYEREK1, GYEREK2):szulo(SZ1, GYEREK1), szulo(SZ2, GYEREK2), (fiu testver(SZ1, SZ2) ; nover(SZ1, SZ2)). A definíció egy szabályból áll, amelynek jobb oldala négy elemi állítást tartalmaz. Ebbõl az elsõ kettõben semmi különös nincs, ellenben az utolsó kettõt közös zárójelbe tettük, és pontosvesszõ (;) szimbólummal választottuk el: (fiu testver(SZ1, SZ2) ; nover(SZ1, SZ2)). Ez azt jelenti, hogy a fiu testver(SZ1, SZ2) és a nover(SZ1,SZ2) részfeladatok között logikai VAGY szerepel a szokásos ÉS helyett. Ezért ezt a szabályt úgy kell értelmezni, hogy Egy személy

unokatertvére egy másik személynek, ha egyikük valamelyik szülõje bátyja vagy nõvére a másikuk valamelyik szülõjének. Így egy állításba tömörítettünk olyan részfeladatokat, amelyek egyébként külön szabályba kerültek volna. A ; szimbólum nélküli unoka testvér definíció ugyanis a következõképpen nézne ki: unoka testver(GYEREK1, GYEREK2):szulo(SZ1, GYEREK1), szulo(SZ2, GYEREK2), fiu testver(SZ1, SZ2). unoka testver(GYEREK1, GYEREK2):szulo(SZ1, GYEREK1), szulo(SZ2, GYEREK2), nover(SZ1, SZ2). • A ; szimbólum használata tehát csak rövidítés, kényelmi eszköz, a kétfajta definíció a program futása szempontjából azonos. 3.6 Gyakorlatok 1. Definiálja a nagynéni és a nagybácsi relációkat! 2. Mi lesz a válasz erre a feladatra: ? not(not(hazastars(anna, robert))). 4. PROGRAM: TERV module terv. body. terv(BEJARAT,AJTO1,ABLAK1,AJTO2,ABLAK2) :elso szoba(BEJARAT,AJTO1,ABLAK1 ), szoba(AJTO2,ABLAK2), atjaras(AJTO1,AJTO2),

not(szemben(ABLAK1,ABLAK2)). elso szoba(BEJARAT,AJTO,ABLAK) :szoba(AJTO,ABLAK), irany(BEJARAT), not(equal(BEJARAT,AJTO)), not(equal(BEJARAT,ABLAK)). szoba(AJTO,ABLAK) :irany(AJTO), irany(ABLAK), not(equal(ABLAK,eszak)), not(equal(AJTO,ABLAK)). atjaras(AJTO1,AJTO2) :- szemben(AJTO1,AJTO2). irany(eszak). irany(del). irany(kelet). irany(nyugat). szemben(eszak,del). szemben(del,eszak) szemben(kelet,nyugat) szemben(nyugat,kelet). equal(X,X). endmod /* terv /. Programfutások: ? terv(BEJARAT, AJTO1, del, AJTO2, del). BEJARAT = eszak AJTO1 = kelet AJTO2 = nyugat Continue (y/n) ? y BEJARAT = nyugat AJTO1 = kelet 1. mindkét ablak délen AJTO2 = nyugat Continue (y/n) ? y BEJARAT = eszak AJTO1 = nyugat AJTO2 = kelet Continue (y/n) ? y BEJARAT = kelet AJTO1 = nyugat AJTO2 = kelet Continue (y/n) ? y NO ? terv(kelet, AJTO1, ABLAK1, AJTO2, ABLAK2). AJTO1 = eszak ABLAK1 = del AJTO2 = del ABLAK2 = kelet Continue (y/n) ? y AJTO1 = eszak ABLAK1 = del AJTO2 = del ABLAK2 = nyugat Continue (y/n) ? y

AJTO1 = eszak ABLAK1 = nyugat AJTO2 = del ABLAK2 = nyugat Continue (y/n) ? y AJTO1 = del ABLAK1 = nyugat AJTO2 = eszak ABLAK2 = del Continue (y/n) ? y AJTO1 = del ABLAK1 = nyugat AJTO2 = eszak ABLAK2 = nyugat Continue (y/n) ? y AJTO1 = nyugat ABLAK1 = del AJTO2 = kelet ABLAK2 = del Continue (y/n) ? y AJTO1 = nyugat ABLAK1 = del AJTO2 = kelet ABLAK2 = nyugat Continue (y/n) ? y NO 2. bejárati ajtó keleten ? terv(kelet, AJTO1, del, AJTO2, del). AJTO1 = nyugat AJTO2 = kelet Continue (y/n) ? y NO 3. elõszobaajtó keleten és mindkét ablak délen 4. ábra Mindkét szoba ablaka délen van 5. ábra Elõszoba ajtaja keleten van 6.ábra Mindkét szoba ablaka délen és az elõszoba ajtaja keleten van 4.1 A program célja • A TERV program két egybenyíló szobából álló építészeti egységet tervez. A két szobát különbözõ irányokból lehet csatlakoztatni, és az ajtók és ablakok elhelyezésének változtatásával lehet különbözõ változatokhoz jutni. 4.2 A

program feltételrendszere • Az építészeti egység két szobából áll. Mindkét szobának van egy ablaka és egy ajtaja Az elsõ szoba ezen kívül még egy kifelé nyíló ajtót is tartalmaz. A szobák a belsõ ajtókón keresztül egybenyithatók. A két ablak nem lehet az építészeti egység ellentétes oldalain, tehát vagy ugyanazon az oldalon vannak, vagy sarkosan. Két ajtó nem lehet ugyanazon a szobafalon. Egy ablak és egy ajtó nem lehet ugyanazon a szobafalon. Ablak nem nyílhat északra. Az irányokat a négy égtáj segítségével adjuk meg.· Egymással ellentétes irányok: észak-dél és kelet-nyugat. 4.3 A program tudásreprezentációja • A szobák, ajtók és ablakok méretei ebben a programban nem fontosak. Vehetünk akármilyen rögzített értékeket Itt az a fontos, hogy milyen változatai lehetnek a két helyiség összekapcsolásának, a szobák mely falaira kerülhetnek az ajtók, ablakok. • A szobák négy falát a négy égtájjal

reprezentáljuk: 7. ábra Egy szoba falai Ha be akarunk tervezni egy ablakot és egy ajtót ebbe a szobába, akkor csak azt kell megadni, hogy mely falakon lesznek. • Nézzük meg a szoba definícióját: szoba(AJTO,ABLAK) :irany(AJTO), irany(ABLAK), not(equal(ABLAK,eszak)), not(equal(AJTO,ABLAK)). A szoba predikátum két argumentumú, tehát egy szobát két paraméterrel jellemezünk. Az elsõ argumentum egy változó az AJTO, amely a négy égtáj valamelyikét veheti fel értékként. Ez fogja az ajtó helyét megadni A második argumentumban lévõ ABLAK változó hasonló szerepet tölt be az ablak helyére vonatkozóan. Például a szoba(kelet, del) azt jelenti, hogy a szobában az ajtó a keleti falon, az ablak a déli falon van. Ezt így ábrázolhatnánk: 8. ábra Ajtók és ablakok jelölése • A két helyiségbõl álló építészeti egységet öt paraméter jellemzi, a terv predikátum öt argumentumú. Az argumentumok rendre a következõk: BEJARAT az elsõ

szoba külsõ ajtaja, AJTO1 az elsõ szoba belsõ ajtaja, ABLAK1 az elsõ szoba ablaka, AJTO2 a második szoba belsõ ajtaja, ABLAK2 a második szoba ablaka, és mind az öt argumentum a négy égtáj valamelyikét veheti fel értékül. 4.4 A leírás és a program egysége • Amikor leírunk egy programot, akkor azt mondjuk meg, hogy mit is várunk tõle. Az elõzõ alfejezetben "A program feltételrendszere" cím alatt tulajdonképpen leírtuk a TERV program specifkációját. Nézzük meg, hogy mennyire közel van a program szövege a leíráshoz! Írjuk egymás mellé a program szövegét és a magyar nyelvû leírást. Program terv(BEJARAT,AJTO1,ABLAK1, AJTO2,ABLAK2):elso szoba(BEJARAT,AJTO1,ABLAK1), szoba(AJTO2,ABLAK2), atjaras(AJTO1,AJTO2), not(szemben(ABLAK1, ABLAK2)). elso szoba(BEJARAT,AJTO,ABLAK):szoba(AJTO,ABLAK),irany(BEJARAT), not(equal(BEJARAT,AJTO)), not(equal(BEJARAT,ABLAK)). szoba(AJTO,ABLAK):irany(AJTO), irany(ABLAK), not(equal(ABLAK,eszak)),

not(equal(AJTO,ABLAK)). Leírás Az építészeti egység két szobából áll. Mindkét szobának van ablaka és egy belsõ ajtaja. Az elsõ szoba ezen kívül még egy kifelé nyíló ajtót is tartalmaz. A szobák a belsõ ajtókon keresztül egybenyithatók. A két ablak nem lehet az építészeti egység ellentétes oldalain. Két ajtó nem lehet ugyanazon a szobafalon. Egy ablak és egy ajtó nem lehet ugyanazon a szobafalon. Ablak nem nyílhat északra atjaras(AJTO1,AJTO2):szemben(AJTO1,AJTO2). irany (eszak). irany (del). irany (kelet). irany (nyugat). Az irányokat a négy égtáj segítségével adjuk meg. szemben (eszak,del). szemben (del,eszak). szemben (kelet,nyugat). szemben (nyugat,kelet). Egymással ellentétes irányok: észak-dél és kelet-nyugat. Az egyetlen mondat, amelynek a programbeli megfelelõje egy kis magyarázata szorul, a következõ: A szobák a belsõ ajtón keresztül egybenyithatók. A megfelelõ programrészlet: atjaras(AJTO1, AJTO2). Nézzük

meg az átjárás részfeladat definícióját: atjaras(AJTO1,AJTO2):- szemben(AJTO1,AJTO2). Ha két szoba egymásba nyílik, akkor az ajtó a közös falon van. Ez a közös fal az egyik szoba szempontjából tekintve például északi, a másik szoba szempontjából tekintve déli, azaz ellentétes irányú (9. ábra) 9. ábra Egybenyíló szobák Egybenyíló szobák esetén a két bélsõ ajtó AJTO1 és AJTO2 egybeesik, tehát tulajdonképpen egy ajtó. Azonban a falakat, és így a bennük lévõ ajtókat is a szobák szempontját ellemezzük, egy közös fal helyett a fal két oldalát (pl. északi és déli oldalát) vesszük figyelembe. Így már érthetõ, hogy miért jelent közös összekötõ ajtót az, ha két szoba belsõ ajtajai ellentétes irányú falban vannak. • A program többi része könnyen érthetõ, és rendkívül hasonlít a leíráshoz. A PROLOG-ot azért tartják nagyon magas szintû nyelvnek, mert sokkal közelebb van a természetes nyelvû

szövegekhez, mint az ún. magas szintû nyelvek (pl PASCAL, C, FORTRAN). Egy jól megírt PROLOG programba sokkal kevesebb természetes nyelvû magyarázatot (commentet) kell beszúrni, mint a hagyományos nyelvû programokba, mivel a program szövege magyarázza önmagát. 4.5 Különbözõ feladatok • Tekintsük az 1. feladat célállítását: ? terv(BEJARAT, AJTO1, del, AJTO2, del). Az öt paraméter közül a harmadik és az ötödik konstans, a többi változó. A feladat azt jelenti, hogy tervezzünk olyan két szobából álló építészeti egység változatokat, ahol mindkét szoba ablaka délre néz. A futási eredmények az ajtók helyét jelölik ki, és ezáltal a szobák közös falai is adódnak. A program futása négy változatot adott eredményül (4 ábra) A 2 feladat, a ? terv(kelet, AJTO1, ABLAK1, AJTO2, ABLAK2). egyetlen konstans paramétere az elõszobaajtó helyét rögzíti keletre, és ehhez kell olyan tervváltozatokat találni, amelyek a program

feltételrendszerét kielégítik. Hét tervváltozatot kaptunk eredményül (5 ábra) Ha kíváncsiak vagyunk, hogy melyik az a tervváltozat, amely az 1. és 2 feladat kívánalmait egyidejûleg teljesíti, egy következõ 3.feladatot kell célállítás formájában megfogalmazni: Tervezzünk olyan két szobából álló építészeti egységet, ahol mindkét ablak délre nyílik, és az elõszobaajtó keleten van. A megfelelõ célállítás: ? terv(kelet,AJTO1,del,AJTO2,del). Ebben a feladatban már csak az átjáró ajtó helyét kell a programnak kijelölnie, minden más rögzített. Eredményül egyetlen változatot kapunk, amely természetesen elõfordult az 1. és a 2 feladat futási eredményei között is (6 ábra) Úgy is mondhatnánk, hogy ezzel a feladattal megkerestük a két elsõ feladat futási eredményeinek a közös részét. 4.6 Gyakorlatok 1. Veszítenénk-e információt, ha elhagynánk a terv(BEJARAT,AJTO1,ABLAK1,AJTO2,ABLAK2) öt argumentumú relációból a

negyedik, AJTO2 argumentumot? A terv szabály a következõképpen módosulna: terv(BEJARAT,AJTO1,ABLAK1,ABLAK2) :elso szoba(BEJARAT,AJTO1,ABLAK1 ), szoba(AJTO2,ABLAK2), atjaras(AJTO1,AJTO2), not(szemben (ABLAK1,ABLAK2)). 2. Módosítsa úgy az elso szoba szabályt, hogy az elsõ szobából még egy ajtó nyíljon 3. Bõvítse ki úgy a programot, hogy az elsõ szobából ne egy, hanem két szoba nyíljon Az új, harmadik szoba ablaka legyen ugyanolyan tájolású, mint a második szoba ablaka. 5. PROGRAM: ÖSSZEG module osszeg. body. osszeg1(ELSO,UTOLSO, VEGOSSZEG) :ELSO < = UTOLSO, ciklus1(ELSO,UTOLSO, 0, VEGOSSZEG). ciklus1(UTOLSO, RESZOSSZEG, VEGOSSZEG) :VEGOSSZEG is UTOLSO + RESZOSSZEG, !. ciklus1(B, UTOLSO, RESZOSSZEG, VEGOSSZEG) :S1 is B + RESZOSSZEG, B1 is B + 1, ciklus1(B1, UTOLSO, S1, VEGOSSZEG). osszeg2(ELSO, UTOLSO, VEGOSSZEG) :ciklus2(ELSO, UTOLSO, 0, VEGOSSZEG). cikÍus2(B, UTOLSO,RESZOSSZEG, VEGOSSZEG) :B < = UTOLSO, S1 is B + RESZOSSZEG, B1 is B + 1,

ciklus2(B1, UTOLSO, S1, VEGOSSZEG). ciklus2(UTOLSO, UTOLSO, RESZOSSZEG, VEGOSSZEG) :VEGOSSZEG is UTOLSO + RESZOSSZEG. endmod /* osszeg / Programfutások: ? osszeg1 (1,5,VEGOSSZEG) VEGOSSZEG = 15 Continue (y/n) ? y NO 1. Számok összege 1-tól 5-ig ? osszeg1(-8,100,VEGOSSZEG). VEGOSSZEG = 5014 Continue (y/n) ? y NO 2. Számok összege 8-tól 100-ig ? osszeg1(100,3,VEGOSSZEG). NO 3. Számok összege csökkenõ sorrendben ? osszeg1(10,10,VEGOSSZEG). VEGOSSZEG = 10 Continue (y/n) ? y NO 4. Egy szám összege ? osszeg1(K,5,VEGOSSZEG). Error: non numeric at 692 < = 5 5. Számok összege valamennyitõl 5-ig ? osszeg(1,K,VEGOSSZEG). Error: non numeric at 692 < = 5 6. Számok összege 1-tól valameddig ? osszeg1(1,5,15). Yes 7. Igaz-e, hogy a számok összege 1-tól 5-ig 15? ? osszeg1(1,5,16). Error: evaluation limit at 24002 is 3326+5529475 Limit = 10000 8. Igaz-e, hogy a számok összege 1-tól 5-ig 16? ? osszeg2(1,5,VEGOSSZEG). 1. az "osszeg2"-re

VEGOSSZEG = 15 Continue (y/n) ? y NO ? osszeg2(1,5,15). Yes 7. az "osszeg2"-re ? osszeg2(1,5,16). NO 8. az "osszeg2"-re 5.1 A program célja • Az ÖSSZEG program egész számokat ad össze növekvõ sorrendben. Bemenõ adatként az elsõ és az utolsó összeadandó szám szerepel, eredményül a rendszer a számok összegét adja. 5.2 A program feltételrendszere • Az ÖSSZEG program egy ciklust megvalósító rekurzív definíció két változatát mutatja be. Mindkét definíció (összegl és összeg2) a következõ rögzített feltételekkel mûködik: 1. A számokat növekvõ sorrendben összegzi 2. Az összeadandó számok egyesével növekednek 5.3 Ciklus szervezése rekurzióval • Az egész összegzõ eljárás olyan feladat, amely szinte minden programozási nyelven könnyedén megoldható. Nem is azért mutatjuk be, hogy ezzel a PROLOG különleges lehetõségeit feltárjuk, hanem azért, hogy bemutassuk, milyen egyszerû a PROLOG-ban megoldani

a ciklusszervezést rekurzióval. • A feladat tehát az, hogy adjuk össze az egész számokat valahánytól valameddig. Nézzük az 1 feladatot: osszeg1 (1,5,VEGOSSZEG). Az összeg1 definíciója a következõ osszeg1(ELSO, UTOLSO, VEGOSSZEG) :ELSO < = UTOLSO, ciklus1(ELSO, UTOLSO, 0, VEGOSSZEG). Ez a szabály azt jelenti, hogy az ELSO értékétõl az UTOLSO értékéig terjedõ egész számok összege VEGOSSZEG, ha az ELSO értéke kisebb vagy egyenlõ az UTOLSO értékével, és a ciklus1 ciklus nulla kezdõértékkel futtatva az ELSO és az UTOLSO közötti számokra VEGOSSZEG-et ad eredményül. • A kisebb vagy egyenlõ relációt a PROLOG-ban beépített predikátummal adhatjuk meg, jele: < =. • Tehát az osszeg1 szabályból hívjuk meg a ciklus1 ciklust, mint az osszeg1 feladat egyik részfeladatát. Az összeg1-nek három argumentuma van, a ciklus1-nek ugyanez a három és még egy negyedik. Az új argumentum sorrendben a negyedik lett, ezért a VEGOSSZEG, azaz a

végeredményt kiadó változó a negyedik helyre csúszott. Ennek az új argumentumnak a szerepe igen fontos. Ide fog kerülni a számok összegzésébõl kapott összeg, mindig annak megfelelõen, hogy az összegzésben hol tartunk. A ciklus meghívásakor az összeg értéke természetesen nulla Nézzük a ciklus1 definícóját: ciklus1 (UTOLSO, UTOLSO, RESZOSSZEG, VEGOSSZEG) :VEGOSSZEG is UTOLSO + RESZOSSZEG, !. ciklus1(B, UTOLSO, RESZOSSZEG, VEGOSSZEG) :S1 is B + RESZOSSZEG, B1 is B + 1, ciklus1 (B1, UTOLSO, S1, VEGOSSZEG). • Ez a definíció két állításból áll. Az elsõ állítás a ciklus leállítását szabályozza, második a ciklus rekurzív újrahívását végzi el. Ez azt jelenti, hogy a rendszer a ciklus minden új meghívásakor ellenõrzi, hogy a végén vagyunk-e már? Ha igen, akkor elvégzi a leálláshoz szükséges utolsó simításokat és befejezi a mûködését. Ha nem, akkor a második állítást alkalmazva belelép a ciklus egy újabb körébe.

A ciklus akkor fog leállni, amikor az elsõ számtól kezdve elszámoltunk már az utolsó számig. Ennek programbeli megfelelõje az, hogy a ciklus1 elsõ argumentuma, amely mindig azt tartalmazza, hogy hányadik számnál tartunk felveszi az utolsó szám, az UTOLSO értékét. Ennek megfelelõen az elsõ állítás bal oldala a következõ: ciklus1(UTOLSO, UTOLSO, RESZOSSZEG, VEGOSSZEG) :Tehát, ha már az utolsó számnál tartunk, a rendszernek sikerül illesztenie a ciklus1 részfeladatot a definíció elsõ állításával. Már csak az van hátra, hogy az összeghez az utolsó számot hozzáadjuk, és ez lesz a végeredmény Ezt az utolsó lépést a következõ részfeladat tartalmazza: VEGOSSZEG is UTOLSO + RESZOSSZEG. Az is (magyarul: van) beépített eljárás, hatására az VEGOSSZEG változó felveszi az UTOLSO és a RESZOSSZEG változók összegét. Az is szerepérõl a következõ, "Aritmetika" címû fejezetben részletesen fogunk beszélni Külön fejzetet

szentelünk a ciklus1 definíciójában lévõ cut-nak is. • Most nézzük a ciklus törzsét, azaz a rekurzív újrahívás szabályát: ciklus1(B, UTOLSO, RESZOSSZEG, VEGOSSZEG) :S1 is B + RESZOSSZEG, B1 is B + 1, ciklus1 (B1, UTOLSO, S1, VEGOSSZEG). A ciklus1 predikátum négy argumentuma rendre a következõ: B UTOLSO RESZOSSZEG VEGOSSZEG szám, ahol az összegzésnél tartunk; az utolsó összeadandó szám; az idáig felgyülemlett részösszeg; a változó, amelybe a végösszeg kerül, ha a ciklus leáll. A ciklusl rekurzív újrahívásakor ebbõl a négybõl kettõ megváltozik. A RESZOSSZEG összeghez hozzáadjuk a soron lévõ számot, és az új összeget S1-nek fogjuk nevezni S1 is B + RESZOSSZEG, és a B szám helyett vesszük a következõ számot: B1 is B+1. Így a ciklus1 rekurzív hívása a ciklus1 (B1, UTOLSO, S1, VEGOSSZEG) paraméterekkel fog megtörténni. • A ciklus1 ciklus leállításával már foglalkoztunk. 5.4 Különbözõ rekurzív definíciók

õsszehasonlítása • A ÖSSZEG program két részprogramból, az osszeg1 és az osszeg2 definíciókból áll, amelyek ugyanannak a feladatnak a megoldására alkalmasak. Melyiket használjuk egy nagy programban? Hogy válaszolni tudjunk erre a kérdésre, vizsgáljuk meg, hogy miben különbözik egymástól a két megoldás. ÖSSZEG1 1. A kezdõ és az utolsó szám viszonyának ellenõrzése cikluson kívül van, azaz az ELSO < = UTOLSO feltétel az összeg1 definícióban van. 2. A ciklus1 ciklus definíciója két állításból áll Az elsõ a ciklus leállítását, a második a ciklus rekurzív újrahívását oldja meg. 3. A ciklus1 ciklus definíciójában van cut ÖSSSZEG2 A kezdõ és az utolsó szám viszonyának ellenõrzése cikluson belül van, azaz a B < = UTOLSO feltétel a ciklus2 definícióban van. A ciklus2 ciklus definíciója két állításból áll. Az elsõ a ciklus rekurzív újrahívását, a második pedig a ciklus leállítását oldja meg. A

ciklus2 ciklus definíciójában nincs cut. A futtatási eredmények között is van egy kis különbség: A két részprogram ugyanazt az eredményt szolgáltatja, kivéve egy esetet: 4. A rendszer végtelen ciklusba esik olyan célállítások futtatásánál, ahol mindhárom argumentum szám, és nem igaz az, hogy az elsõ és második közötti számok összege a harmadik argumentumban megadott szám. Például a 7 feladat: összeg1(1,5,16). Error: evaluation limit 5. Hatékonyság A ciklusl két állítása közül az elsõ a leállító állítás és a második az újrahívó. Ezért, ha a ciklus 100 körbõl áll, akkor a rendszer 100-szor próbál eredménytelenül illeszteni az elsõ állítással. Ez idõbe kerül A rendszer hamissal leáll az ilyen típusú feladatoknál. Például a 7. feladat: összeg2(1,5,16) NO A ciklus2 két állítása közül az elsõ, az újrahívó és a második a leállító. A rendszer az elsõ állítást fogja hasznáIni a rekurzív

újrahíváshoz egészen addig, amíg a cikluson belüli ellenõrzésnél kiderül, hogy a soron lévõ szám már nagyobb, mint az inputként megadott utolsó összeadandó szám: B < = UTOLSO Ekkor a fenti feltétel nem teljesül, a rendszer a ciklusból hamissal kiszáll, és a második, leállító állítással próbál illeszteni. Ez a megoldás hatékonyabbnak tûnik még akkor is, ha a cikluson belül eggyel több feltétel van. Az elõzõ öt pont alapján levonhatjuk azt a következtetést, hogy az osszeg2 megoldás egy kicsit hatékonyabb és jobb megoldás, mint az osszeg1, ezért egy nagy programban célszerû ezt a változatot alkalmazni. 5.5 Aritmetika • Az összes PROLOG rendszer az aritmetikai alapmûveleteket és az aritmetikai összehasonlító relációkat beépített predikátumokkal oldja meg. Ezen beépített predikátumok köre és formája megvalósításonként változik Vannak PROLOG rendszerek, amelyek csak az egész számok aritmetikáját

tartalmazzák, mások a valós számok használatát is megengedik. Az MPROLOG rendszer egész és valós számokra kidolgozott aritmetikával rendelkezik [14], de az IBM PC megvalósításban csak az egész számok aritmetikája mûködik. A valós számok formája a szokásos Például a 4.52e-2 egy valós szám, értéke 0,0452. A valós számok formájának pontos definíciója az MPROLOG kézikönyvben található [14]. Egy aritmetikai kifejezés a különbözõ aritmetikai mûveletek segítségével épülhet fel Még egyszer hangsúlyozni szeretnénk, hogy a beépített eljárások formája megvalósításonként változik, de az aritmetikai mûveletek olyan általánosak, hogy ezek szinte mindegyik PROLOG rendszerben megegyeznek. Az aritmetikai kifejezések a következõ mûveletekbõl épülhetnek fel. Legyen N, N1, N2 aritmetikai kifejezés az egész számokkal, és R, R1, R2 aritmetikai kifejezés a valós számokkal. R1 + R1 R1- R2 +R -R R1 * R2 R1 / R2 N1 div N2 N1 mod N2

R*N int(R) random(R1,R2) round(R) abs(R) max integer min integer max real min real összeadás kivonás pozitív R (R) negatív R szorzás osztás N1 egész osztása N2-vel az N1 div N2 maradéka R az N-edik hatványon a legnagyobb egész szám, amely kisebb vagy egyenlõ R-rel véletlen valós szám generálása R1 és R2 között (határokat is beleértve) kerekítés a legközelebbi egész számra (felfelé, ha középen van) R abszolút értéke a maximális egész szám a minimális egész szám a maximális valós szám a minimális valós szám small real a legkisebb abszolút érték átalakítása valós számmá • Ha egy PROLOG programban egy aritmetikai kifejezés értékét egy változónak szeretnénk átadni, az is beépített eljárást használjuk. Y is (X + 153)/2. Ez azt jelenti, hogy Y értéke legyen az a szám, amelyet akkor kapunk, ha (X + 153)/2 kifejezés értékét kiszámoljuk. Ehhez természetesen az kell, hogy az X valamilyen konkrét szám értéke

legyen, mire idáig eljut a feladatmegoldó algoritmus. Ha ez nem teljesül, akkor a non numeric at X ( nem szám X helyén) hibaüzenetet kapjuk, és a megoldás menete megszakad. Ilyenkor implementációtól függóen különbözõ szolgáltatásokat nyújthat a rendszer a hiba elhárítására (pl. nyomkeresés bekapcsolása, szerkesztõprogram igénybevétele stb) Tehát az is beépített eljárásnak három kimenetele lehet: 1. X is (25 + 153)/2 2. 100 is 5 * 5 3. Y is (X + 153)/2 • sikerül, nem sikerül (visszalépést okozhat), hibaüzenettel megszakas a furás, ha X nem vett fel konkrét értéket. A bépített aritmetikai összehasonlító eljárások a következõk: R1 =:= R2 R1 / = R2 R1 > R2 R1 < R2 R1 > = R2 R1 < = R2 aritmetikai egyenlõ aritmetikai nem egyenlõ Rl nagyobb,mint R2 R1kisebb,mint R2 Rl nagyobb vagy egyenlõ,mint R2 R1kisebb vagy egyenlõ,mint R2 Mind a hat eljárás úgy mûködik,hogy a rendszer a relációjel bal és jobb oldalán

lévõ kifejezéseket külön-külön kiértékeli,és ezután összehasonlítja a két eredményt. Itt is három eset lehetséges: igaz vagy hamis eredményt kapunk, vagy non numeric (nem szám) hibaüzenettel megszakad a futás. 5.6 A cut használata rekurzív hívások leállításánál • Nézzük a ciklus1 definícióját még egyszer: ciklus1(UTOLSO, UTOLSO, RESZOSSZEG, VEGOSSZEG):VEGOSSZEG is UTOLSO + RESZOSSZEG, !. ciklus1(B, UTOLSO, RESZOSSZEG, VEGOSSZEG):S1 is B + OSSZEG, B1 is B + 1, ciklus1(B1, UTOLSO, S1, VEGOSSZEG). • Az elsõ, a ciklust leállító állításban van egy cut (!). Ennek a cutnak a regisztrálássára akkor kerül sor, amikor a ciklus végig futott, már az utolsó simításokkal is készen vagyunk, és a VEGOSSZEG változóban ott van a végeredmény. Miért van erre szükség? Ahhoz, hogy erre a kérdésre válaszolni tudjunk, futtassuk le az osszeg1(1,5,VEGOSSZEG) célállítást úgy, hogy a cut szimbólumot hagyjuk ki a programból. A rendszer

szépen ki fogja számítani VEGOSSZEG értékét, majd megkérdezi, hogy folytassa-e az új lehetõségek feltárását: VEGOSSZEG = 15 Continue (y/n) ? • Ha a kérdésre y begépelésével, azaz igennel válaszolunk, csodálkozva fogjuk tapasztalni,hogy valami baj történik: a rendszer fut,fut,fut, és nem hajlandó leállni, csak az elõre rögzített híváskorlát elérésekor az Error: evaluation limit üzenettel. • Hogyan sikerült a rendszert végtelen ciklusba kergetni a visszalépéssel? Amikor utoljára hívtuk meg a ciklus1 részfeladatot, a paraméterek értékei a következõk voltak: ciklus1(5,5,10,VEGOSSZEG) Az elsõ állítással sikerült az illesztés,és a VEGOSSZEG is UTOLSO + RESZOSSZEG feltételt is teljesítve megkaptuk a VEGOSSZEG=15 értéket. Innen lép vissza a rendszer, amikor a Continue (y/n) ? kérdésre az y begépelésével válaszolunk, és megpróbálja a ciklus1(5,5,10, VEGOSSZEG) részfeladatot a második állítással illeszteni.

ciklus1(B, UTOLSO, RESZOSSZEG, VEGOSSZEG):S1 is B + RESZOSSZEG, B1 is B + 1, ciklus1 (B1, UTOLSO, S1, VEGOSSZEG). Ez sikerül is, és az illesztés során B = 5, UTOLSO = 5, RESZOSSZEG = 10 értékátadás történnek. A következõ két feltétel teljesítése során az S1 és a B1 változók is értéket kapnak: S1 is 5+ 10, B1 is 5 + 1, és így ciklus1 újrahívása a következõ paraméterekkel történik meg: ciklus1(6, 5,15, VEGOSSZEG). Ezt a részfeladatot csak a második állítással tudjuk illeszteni, hiszen az elsõ paraméter értéke nem egyenlõ az utolsó összeadandó számmal, azaz 5-tel. Az elsõ paraméter értéke minden újabb hívásnál csak növekedni fog, így tehát semmi esélyünk a ciklus leállítására. Ez bizony végtélen ciklus • A cut jelenléte ezen a gondon úgy segít, hogy letiltja a ciklus leállása utáni visszalépést. Mintha ezt mondaná: Ha egyszer sikerült a ciklust tisztességesen leállítva végigfuttatni, akkor semmi értelme egy

esetleges késõbbi visszalépésnek a ciklus belsejébe. • A cut (!) használata az ilyen típusú rekurzív definíciókban nagyon is indokolt, és ajánlatos. 5.7 Gyakorlatok 1. Módosítsa úgy az osszeg2 definícióját, hogy az elsõ számtól számított minden 5 számot adja össze növekvõ sorrendben. Ellenõrizze, hogy az utolsó és az elsõ szám különbsége osztható-e 5-tel! 2. Módosítsa úgy az osszeg2 definícióját, hogy az a számokat csökkenõ sorrendben adja összel! 3. Bõvítse úgy az osszeg2 definícióját, hogy az a számokat növekvõ és csökkenõ sorrendben is össze tudja adni! 6. PROGRAM: TÉGLALAP module teglalap. body. metszet(A,B) :teglalap(A,R1), teglalap(B,R2), metsz(R1,R2,R3), nl, write(R3). metszet(A,B) :nl, write("Diszjunktak.") metsz(r(p(X1,Y1),p(X2,Y2)), r(p(K1,L1 ),p(K2,L2)), r(p(M1,N1),p(M1,N2))) :- szamol(X1,X2,K1,K2,M1,M2), szamol(Y1,Y2,L1,L2,N1,N2). szamol(A,B,C,D,X,Y) :max(A,C,X), min(B,D,Y), min(X,Y,X). min(X,Y,X)

:- X < Y. min(X,Y,Y). max(X,Y,X) :- X > Y. max(X,Y,Y). teglalap(1,r(p(4,2),p(8,6))). teglalap(2,r(p(6,10),p(10,15))) teglalap(3,r(p(7,8),p(15,12))). teglalap(4,r(p(13,4),p(18,14))) teglalap(5,r(p(5,3),p(6,5))). teglalap(6,r(p(15,3),p(18,8))). endmod /* teglalap /. Programfutások: ? teglalap(N, r(p(6,10), CSUCS2)). N=2 CSUCS2 = p(10,15) Continue (y/n) ? y NO 1. feladat ? teglalap(N, r(CSUCS1, p(18,Y))). N=4 CSUCS1 = p(13,4) Y = 14 Continue (y/n) ? y N=6 CSUCS1 = p(15,3) Y=8 Continue (y/n) ? y NO 2. feladat ? metszet(2,3). r(p(7,10),p(10,12)) Yes 3. feladat ? metszet(3,X). r(p(7,10),p(10,12)) X=2 Continue (y/n) ? y 4. feladat r(p(7,8),p(15,12)) X=3 Continue (y/n) ? y r(p(13,8),p(15,12)) X=4 Continue (y/n) ? y r(p(15,8),p(15,8)) X=6 Continue (y/n) ? y Diszjunktak. Yes. ? metszet(1,X). r(p(4,2),p(8,6)) X=1 Continue (y/n) ? y 5. feladat r(p(5,3),p(6,5)) X=5 Continue (y/n) ? y Diszjunktak. Yes. ? metszet(18,3). Diszjunktak. Yes 6. feladat 10. ábra Téglalapok 6.1 A

program célja •A TÉGLALAP program a koordináta-rendszer tengelyeivel párhuzamos oldalú téglalapok metszetét számítja ki. A program adatbázisában a téglalapok sorszámot kaptak. Bemenõ adatként két téglalap sorszáma szerepel, eredményként a metszetük koordinátáit nyomtatja ki a program. Ha a két téglalap nem metszi egymást, akkor "Diszjunktak" üzenetet ír ki a program. 6.2A feladat feltételrendszere • Két téglalap vagy metszi egymást egy harmadik teglalapban, vagy diszjunktak. A metszet téglalap határesetekben szakasszá, illetve ponttá is elfajulhat. 11. ábra Téglalapok metszetének négy esete A metszet téglalapot koordinátáival határozzuk meg. • Az abszcissza értékek kiszámításához a két téglalap x tengellyel párhuzamos oldalait párhuzamos eltolással egy egyenesre illesztjük, és a két szakasz metszetét meghatározzuk. A metszet két végpontja határozza meg a keresett abszcissza értékeket. Hasonló módon

számítjuk ki az ordináta értékeket • Egy téglalapot két, átlós irányú - bal alsó és jobb felsõ - csúcspontjával, egy csúcspontot pedig a két koordinátájával adjuk meg. 6.3 Összetett kifejezések • A korábbiakban már említettük, hogy egy egyszerû kifejzés az vagy változó,vagy konstans. Ebben a fejezetben az összetett kifejezések elsõ csoportjával ismerkedhetünk meg, amelyekben egy függvényjel vagy funktor után kerek zárójelek között néhány kifejezés szerepel vesszõvel elválasztva. Például az r(CSUCS1,CSUCS2) egy összetett kifejezés. Az r egy függvényjel,a CSUCS1 és CSUCS2 két változó, azaz két kifejezés Úgy is szokás mondani,hogy az r két argumentumú függvény. Az argumentumok száma természetesen tetszõleges Az ilyen függvényeket nem kell külön deklarálni, csak egyszerûen beírjuk a programba, és használjuk õket, pont úgy, mint a predikátumokat. Az elõzõ példánkban a CSUCS1 és CSUCS2 változók

helyett szintén állhat összetett kifejezés.Például: r(p(4,2),p(8,6)). A p szintén két argumentumú függvény. Ezzel az összetett kifejezéssel egy téglalapot adtunk meg, ahol a CSUCS1 és a CSUCS2 a két csúcspont, és egy-egy csúcspontot a két koordinátája határoz meg. p(X,Y). • A program adatbázisa tárolja a téglalapokat a teglalap tényállítások formájában. Például: teglalap(1, r(p(4,2),p(8,6))). • A teglalap tényállításnak két argumentuma van. Az elsõ a téglalap sorszáma, a második a téglalapot megadó összetett kifejezés. 6.4 Összetett kifeiezések illesztése • Az összetett kifejezések illesztési szabályai a lehetõ legtermészetesebbek: 1. Egy összetett kifejezés és egy változó illeszthetõ 2. Egy összetett kifejezés és egy konstans nem illeszthetõ 3. Két összetett kifejezés akkor illeszthetõ, ha függvényjelük és argumentumaik száma megegyezik, valamint az azonos pozícióban lévõ argumentuaik

egymással illeszthetõk. Például: p(3,4) és X p(3,4) és 12 p(3,4) és r(3,4) p(V1,V2) és r(p(X,Y),p(Z,W)) illeszthetõ, nem illeszthetõ, nem illeszthetõ, illeszthetõ. • A PROLOG-beli mintaillesztés óriási programozási lehetõségeket nyújt az összetett kifejezésekkel kapcsolatban.A nagy trükk az, hogy egy összetett kifejezés bármely részkifejezése lehet változó, és a mintaillesztés automatikusan értéket ad minden változónak, ha konstanssal illeszkedik. Nem kell a bonyolult struktúrában keresgélni, mindezt elvégzi helyettünk a PROLOG mintaillesztése. Vizsgáljuk meg, hogyan használjuk ki ezt a lehetõséget, amikor a téglalapok adatbázisára vonatkozó feladatokat fogalmazzuk meg! Tekintsük a programfutások közül az l. feladatot: ? teglalap(N,r(p(6,10),CSUCS2)). Ez a célállítás a következõ kérdést jelenti: Melyik az a téglalap,amelynek a bal alsó csúcsponja a (6,10) koordinátapár? Hol van ennek a téglalapnak a jobb felsõ

csúcsponja? PROLOG rendszer a választ könnyedén megadja: N=2 CSUCS2 = p(10,15) Nézzük a 2.feladatot: ? teglalap(N,r(CSUCS1,p(18,Y))). Ezt a célállítást pedig így lehetne szavakkal megfogalmazni: Melyek azok a téglalapok, amelyek jobb függõleges oldalai a 18-as abszcissza értéken vannak? Hol vannak a téglalapok csúcspontjai? Erre a kérdesre a rendszer két eredményt is adott: N=4 CSUCS1 = p(13,4) Y = 14 Continue (y/n) ? y N=6 CSUCS1 = p(15,3) Y=8 Continue (y/n) ? y NO És még számos változata lehet a kérdéseknek, amelyeket a különbözõ célállításokkal meg tudunk fogalmazni. Ha hagyományos nyelven programoznánk, akkor minden új típusú kérdés, és az azokra adandó válasz megadásának módszere külön programozási munkával járna. Ez is dícséri a PROLOG rendszer intelligenciáját 6.5Téglalapok metszete • A koordináta-rendszer tengelyeivel párhuzamos téglalapok metszetét igen egyszerû kiszámítani. A 12ábrán két metszõ

téglalapot láthatunk. Az egyik téglalap átellenes csúcspontjai p(X1,Y1) és p(X2,Y2), a másiké p(K1,L1) és p(K2,L2). A metszet téglalap csúcspontjai: p(M1,L1) és p(M2,L2). 12. ábra Egymást metszõ téglalapok Vetítsük le a két téglalapot az x tengelyre! Az elsõ téglalap képe az X1X2 szakasz, a másodiké a KlK2 szakasz lesz. Az ábráról könnyen leolvasható, hogy a két szakasz metszete K1 X2 szakasz lesz. • A metszet szakasz végpontjait maximum-minimum kereséssel fogjuk megállapítani, amely általános megoldást ad az összes lehetséges esetre (13. ábra) Vegyük a két szakasz bal oldali értékei közül a maximumot, és nevezzük el M1-nek, majd vegyük a jobb oldali értékek közül a minimumot, és nevezzük el M2-nek: max(X1,K1,M1), min(X2,K2,M2). M1M2 lesz a kimetszett szakasz, ha létezik. 13. ábra Szakaszok Arról, hogy a kimetszett szakasz létezik-e, könnyen meggyõzõdhetünk, ha megvizsgáljuk az M1 és M2 abszcissza értékek

egymáshoz való viszonyát: 1. M1 < M2 2. M1 = M2 3. M1 > M2 ekkor a szakasz létezik (a vagy b eset), ekkor a szakasz ponttá zsugorodik (c vagy d eset), a kimetszett szakasz nem létezik, XlX2 és KlK2 szakaszok diszjunktak (e vagy f eset). Mindhárom eset magába foglalja a min(M1,M2,M1) feltétel, mégpedig úgy, hogy az 1. és a 2 esetben igazat, míg a 3 esetben hamisat ad. Ezt a gondolatmenetet a következõ programrészlet valósítja meg: szamol(A,B,C,D,X,Y) :max(A,C,X), min(B,D,Y), min(X,Y,X). A szamol definíció X1, X2, K1, K2, M1, M2 változók szerepkörében rendre az A, B, C, D, X, Y változókat használja. Ez a szabály nemcsak az x, hanem az y tengellyel párhuzamos oldalszakaszok metszetét is kiszámítja, azaz az Y1, Y2, L1, L2, N1, N2 változókkal is meghívjuk. Ezért használunk a definícióban a két konkrét változó sorozattól eltérõ változókat. A min és a max definíciók a rendszerbe beépített aritmetikai összehasonlító

relációkat használják, amelyekkel már megismerkedhettünk az elõzõ fejezetben. 6.6 Gyakorlatok 1. Definiálja három téglalap metszetét! harom metszete(A,B,C,R) ? 2. Írjon egy olyan szabályt, amely egy téglalap négy csúcspontját határozza meg az adatbázisban rögzített adatok alapján! 14. ábra A téglalap csúcspontjai csucsok(N,A,B,C,D) ? 3. Határozza meg két téglalap közös csúcspontját, ha van! Segítség: módosítsa a számol definíciót! 7. PROGRAM: RAJZ module rajz. body. abra(N):kezdet(1,3), rajz(N). kezdet(SZIN, TOLLSZIN ):screenmode < < < graphic, reset, reset heading, color < < < SZIN, pencolor < < < TOLLSZIN. rajz(N):teglalap(N, r(p(X1,Y1),p(X2,Y2))), Z1 is X1*40, W1 is (Y140)+100, Z2 is X240, W2 is (Y2*40) + 100, X is Z2-Z1, Y is W2-W1, teglalap vonala(Z1,W1,X,Y), folytat. teglalap(l,r(p(4,2),p(8,6))). teglalap(2,r(p(6,10),p(10,15))). teglalap(3,r(p(7,8),p(15,12))). teglalap(4,r(p(13,4),p(18,14))).

teglalap(5,r(p(5,3),p(6,5))). teglalap(6,r(p(15,3),p(18,8))). teglalap vonala(Z1,W1,X,Y):position < < < [Z1,W1,0], pen < < < down, forward(X), turn(90), forward(Y), turn(90), forward(X), turn(90), forward(Y), reseLheading. folytat:position < < < [200,20,0], text("folytassuk? (i/n)"), read token(X), valasz(X). valasz(n):clear graphic screen, screenmode < < < text, cut input, ! valasz(i):- cut input, !. valasz(X):- cut input, folytat. vege:clear graphic screen, screenmode < < < text. endmod /* rajz /. Programfutások: ? abra(3). egy téglalap 15. ábra Egy téglalap *i Yes ? rajz(2). két téglalap 16. ábra Két téglalap *i Yes ? rajz(1). *i Yes ? rajz(4). *i Yes ? rajz(5). *i Yes ? rajz(6). sok téglalap *n Yes 17. ábra Sok téglalap 7.1 A program célja • A RAJZ program az adatbázisban tárolt téglalapok egyenkénti kirajzolására szolgál. Bemenõ adatként a téglalap sorszámát kell megadni. A képernyõ és

ábra színei kívánság szerint módosíthatóak 7.2 A feladat feltételrendszere • A RAJZ program feltételrendszerét leginkább az alkalmazott grafika adottságai szabják meg, amelyeket a következõ fejezet ismertet részletesen. Itt csak azokat a feltételeket említjük, amelyek a program szövegébõl egyértelmûen kiderülnek. • Egy téglalap képének kirajzolásához elõször a megfelelõ grafikus környezetet kell megteremteni. Induláskor a képernyõt át kell állítani ·szöveges üzemmódból grafikus üzemmódba, ki kell választani a képernyõ és az ábra szineit, majd az összes többi grafikus állapotjelzõt alapállapotba kell beállítani. A téglalap kirajzolásához szükséges adatokat, a hosszát, a magasságát, valamint a képernyõn elfoglalt helyét az bázisban rögzített csúcsponti koordinátákból számítjuk ki. Az elsõ téglalap kirajzolása után ha a "folytassuk" kérdésre igennel válaszolunk, új téglalapot

rajzolhatunk. Ha nemmel válaszolunk, a képernyõ átmegy szöveges üzemmódba, és a program leáll. 7.3 Grafika • A grafika szó - számítógépes - környezetben a számítógépes képi megjelenítés eszközeinek gyûjteményét jelenti. Sok program teljesen élvezhetetlen képi megjelenítés nélkül, ilyenek a geometriai feladatok és a mûszaki tervezést segítõ programrendszerek. A téglalapok metszetét kiszámító oktatóprogramunk eredményeit is akkor tudjuk a legkönnyebben ellenõrizni, ha kirajzoljuk a szóban forgó téglalapokat. Tulajdonképpen erre a célra írtuk a RAJZ programot • Az IBM PC MPROLOG rendszere jól kidolgozott graifkával rendelkezik, Domán András munkája [6]. A grafikus megjelenítõ programok ugyanolyanok, mint a többi program, csak használják a beépített grafikus eljárásokat. Az MPROLOG grafika részletes leírása megtalálható a kézikönyvekben [16], mi itt csak annyit fogunk leírni belõle, amennyi a programok

elmagyarázásához kell. • Az MPROLOG grafikát sas grafikának nevezik. Pontok, vonalak, háromdimenziós geometriai formák rajzolására alkalmas. A sas egy képzeletbeli madár, amely a háromdimenziós térben mozog, és a sas mozgása (azaz a mozgásban lévõ sas csõre) nyomot hagyhat a képernyõn, ez a nyom a rajz, a térbeli mozgás vetülete. Ha csak síkbeli mozgást akarunk ábrázolni, akkor egyszerûen nem vesszük fgyelembe a ·tér három koordinátája közül a harmadikat. Térbeli formák ábrázolásánál választhatunk az axonometrikus és a perspektívikus megjelenítési formák közül. A sas geometriájában két koordináta-rendszer van: egy abszolút ortogonális koordináta-rendszer, és a sas saját koordinátarendszere, amelyet a hátán hordoz, akárhol is van. Mindkettõ igen fontos, ügyes grafikus programok írásához 7.31 Grafikus állapotjelzõk • A sas aktuális helyét és állapotát a térben ún. állapotjelzõk írják le Ezeket az

állapotjelzõket be lehet állítani, illetve változtatni lehet a program futása során. Csak azokat az állapotjelzõket vizsgáljuk meg részletesen, amelyek a síkbeli ábrák kirajzolásához szükségesek, hiszen mi csak ezeket fogjuk használni. Állaptjelzõ position heading pen color pencolor palette scale screensize screenmode Értéke alaphelyzetben (pozíció) (irány) (toll) (a háttér színe) (a toll színe) (paletta,színskála) (skála) (a képernyõ mérete) (a képernyõ üzemmódja) [0,0,0] x tengely irányában jobbra down (lent) 0 (fekete) 3 (fehér) 1 [1024,320] [4,3] text (szöveges) A position a sas helyét jelöli ki az abszolút koordináta-rendszerben, alaphelyzetben [0,0,0], azaz az origo. A heading azt definiálja, hogy merre néz a sas, ez nagyon fontos akkor, amikor relatív mozgást írunk elõ (pl.: röpülj elõre 50 egységet!). A pen két állapottal rendelkezik. Lehet lent, ekkor nyomot hagy a mozgása, és lehet fent, ekkor nem A palette,

a color és a pencolor a rajz színválasztékát, a háttér és a toll színeit határozzák meg. A scale állapotjelzõ a képernyõ logikai és fizikai skálabeosztásának számpárját állítja be, alaphelyzetben 1024 logikai egységre 320 fizikai egység jut. A scale megváltoztatásával lehet kicsinyíteni, illetve nagyítani az ábrát A screensize állapotjelzõ a képernyõ vízszintes és függõleges méreteinek arányát állítja be, alaphelyzetben 4:3. A megváltoztatásával lehet a képet torzítani. Végül a screenmode állapotjelzõ a képernyõ üzemmódját állítja be, lehet szöveges vagy grafikus. Rajzolni csak grafikus üzemmódban lehet. • Az állapotjelzõk kezelésére két beépített operátort használunk. Jelöljük A-val az állapotjelzõk valamelyikét és X-szel az értékét. A>>>X ellenõrzi, hogy az A állapotjelzõ jelenlegi értéke illeszthetõ-e X-szel. A<<<X pedig egyszerûen X értéket ad az A állapotjelzõnek.

• Nézzük meg : RAJZ programunkat. A vonal szabály elsõ részfeladata: position < < < [Z1,W1,0]. Ez azt jelenti, hogy sast (azaz a tollat) elküldtük a 0 magasságban lévõ sík [Z1,W1] koordinátapárja által meghatározott pontba. Feltételezzük, hogy a Z1 és W1 változókban szám érték van, ha nem, hibajelzést kapunk Mivel csak síkbeli ábrákkal foglalkozunk, a position harmadik koordinátája mindig nulla lesz. A vonal szabály második részfeladata pen < < < down. Ez egyszerûen a tollnak a papírra eresztését jelenti, ezután a toll mozgása nyomot fog hagyni. 7.32 Színek • Két dolog színét változtathatjuk ebben a grafikában, a képernyõ azaz a háttér színét, és az ábra, azaz a toll színét. A színeket egész számokkal jelöljük, ezeket a számokat logikai színeknek is szoktuk hívni. A háttér színét a color állapotjelzõ állítja be, és változtatja meg szükség szerint. A háttér színválasztéka a következõ:

0 fekete 1 kék 2 zöld 3 türkizkék 4 piros 5 lila 6 barna 7 világosszürke 8 sötétszürke 9 világoskék 10 világoszöld 11 világos türkizkék 12 rózsászín 13 halványlila 14 sárga 15 fehér • A toll, azaz a rajz és a grafikus szöveg szineit a pencolor állapotjelzõvel lehet szabályozni. Egyszerre négy különbözõ színû tollunk lehet, de két különbözõ színkészletbõl, azaz palettából választhatunk. 0. paletta l. paletta 0 az aktuális háttér színe 1 zöld 2 piros 3 sárga 0 az aktuális háttér színe 1 türkizkék 2 lila 3 fehér Azt, hogy melyik színnégyessel akarunk rajzolni, a palette állapotjelzõvel lehet megadni. Alaphelyzetben az 1 paletta színei lesznek az érvényesek. Nézzük meg a RAJZ program kezdet szabályát: kezdet(SZIN, TOLLSZIN):screenmode < < < graphic, reset, reset heading, color < < < SZIN, pencolor < < < TOLLSZIN. Ez a szabály a megfelelõ grafikus korülményéket teremti meg a

téglalap kirajzolásához. A képernyõt grafikus üzemmódba váltja (screenmode < < < graphic), majd a reset beépített eljárás hatására letörli a képernyõt és alapértéket ad a paletta (palette < < < 1), pozíció (position < < < [0,0,0]) és toll (pen < < < down) állapotjelzõknek. A reset heading a sas irányát az x tengellyel párhuzamosan jobbra állítja be. Ezek után kerül sor a színek kijelölésére. Feltételezve, hogy a kezdet szabályt két számértékkel hívjuk meg, a color < < < SZIN háttér színét, a pencolor < < < TOLLSZIN toll színét jelöli ki. Például a kezdet(1,3). a SZIN = 1 (kék) és a TOLLSZIN = 3 (fehér) értékátadások következtében kék lapon fehér vonalakat eredményez. 7.33 Mozgások • A sas mozgását beépített eljárások alkalmazásával lehet irányítani. Vizsgáljuk meg elõször azokat az eljárásokat, amelyek a sas éppen aktuális helyzetébõl történõ

elmozdulás módját írják elõ. Ezek a forward (elõre) back (hátra) turn (fordul) twist (csavar) tilt (billen) eljárások. Ezeket a mozgásokat relatív mozgásoknak nevezzük. A forward és a back mindegyike egy argumentumú eljárás Argumentumuk egy szám, amely azt írja elõ, hogy hány egységet repüljön a sas elõre vagy hátra ahhoz a helyzethez képest, amelyben éppen van. A turn, a twist és a tilt a három koordinátatengely körüli forgást szabályozza (18 ábra) 18. ábra A három koordinátatengely körüli forgatás Ha az xy síkban akarunk rajzolni, akkor a turn eljárást kell használnunk. A forward és a turn eljárások összehangolt alkalmazásával bármilyen egyenes vonalakból álló síkbeli ábrát ki tudunk rajzolni, csak arra kell vigyázni, hogy a megfelelõ alkalmakkor felemeljük, illetve leeresszük a tollat. Nézzük meg a RAJZ program teglalap vonala nevû szabályát, amely egy téglalapot rajzol teglalap vonala(Z1,W1,X,Y):position <

< < [Z1,W1,0], pen < < < down, forward(X), turn(90), forward(Y), tum(90), forward(90), forward(Y), reset heading. Atéglalap rajzolását a bal alsó csúcspontban kezdjük (19.ábra) Ide küldjük a tollat a position < < <[Z1,W1,0] beépített eljárás segítségével, és a tollat leresztjük a papírra. 19. ábra A téglalap vonala Ha most kiadjuk a forward(X) utasítást, akkor a toll párhuzamosan az x tengellyel jobbra elindulva X hosszúságú vonalat fog húzni, feltételezve, hogy X pillanatnyi értéke egy szám. Ekkor a turn(90) utasítás hatására 90 fokot fordul (az óramutató járásával ellentétes irányba), és Y hosszúságú vonalat húz. (Ha ellenkezõ irányba szeretnénk fordítani, a turn(-90) eljárást kellene használni.) Az utolsó vonal, a bal függõleges oldal megrajzolása után a toll iránya függõlegesen lefelé mutat, ezért a reset heading beépített eljárással alapállapotba hozzuk. A téglalap kirajzolását

ezzel befejeztük. • A relatív mozgásokat leíró beépített eljárások rendkívül hasznosak, mert könnyû velük általános szabályokat megfogalmazni. A teglalap vonala szabály is általános, hiszen segítségével bármilyen olyan téglalapot ki tudunk rajzolni, amelynek a bal alsó csúcsponti koordinátái adottak, valamint ismert a magassága és a hossza. • A sas grafika abszolút koordináta-rendszerében a move(N1,N2,N3) eljárással tudunk vonalat húzni, ahol N1, N2 és N3 annak a pontnak a koordinátái, ahová a vonalat húzzuk a toll aktuális pozíciójából. Ezt az eljárást a RAJZ program nem használja, csak a teljesség kedvéért említettük meg 7.34 Skálabeosztás • Egy ábra grafikus megjelenítéséhez nagyon fontos, hogy milyen skálabeosztást használ. Két dolgot kell összhangba hozni, egyrészt adva van a feladat világa a megfelelõ méretekkel, másrészt adottak a képernyõ fizikai megjelenítõ képességei. Esetünkben a szokásos

IBM XT konfigurációhoz tartozó színes, grafikus display felbontó képessége 320 * 200 képpont. Ez azt jelenti, hogy ennyi fizikai egység van a képernyõn • Amikor programot írunk, szeretünk a saját léptékünk szerint méretezni. Ezért rendszerint nem a fzikai egységekben, hanem ún. logikai egységekben dolgozunk A sas grafikában a scale állapotjelzõvel lehet definiálni a logikai egységek és fizikai egységek közti összefüggést (alapállapotban 1024:320). Mivel a képernyõ vízszintes és függõleges hosszának aránya alapállapotban 4:3, a képernyõ logikai skálabeosztása: 20. ábra A képernyõ skálabeosztása • Vizsgáljuk meg az ábrázolandó képnek az adatbázisban tárolt koordinátáit. Vízszintes irányban a legnagyobb koordináta érték 18, függõlegesen 15. Ha ilyen kis számokkal, mint logikai egységekkel programoznánk, élvezhetetlenül apró ábrát kapnánk. Ezért az adatbázis koordináta-rendszerében egy fizikai egységet

40 logikai skálabeosztásnak feleltetünk meg. Így kellemes nagyságú téglalapokat fogunk kapni, és a teljes kép ki fog férni, hiszen 18 * 40 = 720 kisebb, mint 1024, 15 * 40 = 600 kisebb, mint 768. • Igen ám, de van még egy kis probléma. A téglalapok kirajzolásán kívül szeretnénk a képernyõ aljára kiírni egy egysoros szöveget, a kérdést, hogy folytassuk-e a téglalapok kirajzolását (folytassuk? (i/n)). Ezt a RAJZ programban úgy oldottuk meg, hogy a téglalapok képét felcsúsztattuk 100 logikai egységgel. A kép még így is elfér, mert a legnagyobb ordináta értéket, a 600-at, megnövelve 100-zal, még mindíg nem léptük túl a megengedett 767-et. Nézzük meg a rajz(N) szabályt, amely az N sorszámu téglalap bal alsó csúcspontjának helyét jelöli ki a képernyõn, valamint kiszámítja a téglalap magasságát és hosszát: rajz(N):teglalap(N, r(p(X1,Y1),p(X2,Y2))), Z1 is X1*40, W1 is (Y140) + 100, Z2 is X240, W2 is (Y2*40) + 100, X is Z2-Z1,

Y is W2-W1, teglalap vonala(Z1,W1,X,Y), folytat. 7.35 Grafikus szöveg • Grafikus üzemmódban is írhatunk ki szöveget a képernyõre, erre szolgál a text beépített eljárás, amelynek egyetlen argumentuma van, egy idézõjelek közé zárt szöveg (PROLOG terminológiában füzérnek nevezzük). A szöveg a sas aktuális pozíciójánál fog kezdõdni, ezért célszerû a text meghívása elõtt a position állapotjelzõt a megfelelõ értékre beállítani: folytat:position < < < [200,20,0], text("folytassuk? (i/n)"), read token(X), valasz(X). Ha a kérdésre válaszolunk, a begépelt szöveg megjelenik a képernyõ alján, a részére fenntartott helyen. A válaszokat a valasz definícióval ellenõrizzük: valasz(n):clear graphic screen, screenmode < < < text,.cut input, valasz(i):- cut input, !. valasz(X):- cut input, folytat. Ha a válasz nem (n), akkor a program letörli a képernyõt, visszavált szöveges üzemmódba és leáll. Ha a

válasz igen (i), akkor új célállítást adhatunk a programnak. Ha valami egyéb választ adunk, akkor a program makacsul újra felteszi az eredeti kérdést, folytassuk-e? Egészen addig, amíg nemmel vagy igennel nem válaszolunk. • A valasz definíció mindhárom szabályában szerepel egy cut input beépített eljárás. Ez az eljárás a bemenõ folyamban végbemenõ esetleges visszalépéseket tiltja le. A visszaléptethetõ bemenõ folyamatról a 8 fejezetben fogunk írni. Ezen kívül a válasz elsõ két szabályában cut szimbólumot is használunk, hogy letiltsunk mindenféle visszalépést a definíción belül. 7.4 Gyakorlatok 1. Milyen hibát okozna, ha a teglalap vonala szabályból elhagynánk a reset heading beépített eljárást? Segítség: Gondoljunk arra, hogy általában egynél több téglalapot szeretnénk rajzolni. 2. Írjon egy olyan szabályt, amely az adatbázis összes téglalapját kirajzolja! Használja fel a fail által kiváltott visszalépéses

módszert! 3. Kétszeres nagyítást vagy kicsinyítést érünk-e el azzal, ha a scale állapotjelzõ alapállapotát: scale < < < [1024 ,320] megváltoztatjuk scale < < < [1024 ,160] -ra? Mi lesz a hatása a scale < < < [2048,320] használatának? 4. Nem kényelmes dolog színek helyett számokat megnevezni Bõvítse a programot olyan adatbázis definíciókkal, amelyek a színek kódjához a színek nevét rendeli. Módosítsa úgy a programot, hogy az abra célállításban a téglalap sorszámát követve megadhassuk a képernyõ és a toll színét szavakkal! 8. PROGRAM: SZÍNEZ module szinez. body. szinez:screenmodi < < < text, nl, write("Jo npot ! Segiteni szeretnek szineket "), write("valogatni "), nl, hatter(SZIN), toll(TOLLSZIN), kezdet(SZIN, TOLLSZIN), teglalap vonala(300,200,260,180), vege. hatter(SZIN):- menu1,ellenor(SZIN,0,5). toll(TOLLSZIN):- menu2, ellenor(TOLLSZIN,0,3). menu1 :- nl, write("Milyen legyen a

hatter szine?"), nl, write(" 0. fekete "), nl, write(" 1. kek "), nl, write(" 2. zold "), nl, wrrte(" 3. turkiz "), nl, write(" 4. piros "), nl, write(" 5. lila "), nl, write("Kerem a valasztott szin sorszamat."), nl menu2:- nl, write("Milyen legyen a toll szine?"), nl, write(" 0. hatter "), nl, write(" 1. turkiz "), nl, write(" 2. lila "), nl, write(" 3. feher "), nl, write("Kerem a valasztott szin sorszamat:"); nl. ellenor(N, TOL, IG):read token(N), N is a integer, N > = TOL, N < = IG, cut input, !. ellenor(N, X, Y):- hiba, ellenor(N, X, Y). hiba:- read token(X), cut input, nl, write(" Hibas szam. Probalja ujra!"), nl kezdet(SZIN,TOLLSZIN):screenmode < < < graphic, reset, reset heading, color < < < SZIN, pencolor < < < TOLLSZIN. teglalap vonala(Z1,W1,X,Y):position < < < [Z1,W1,0], pen < <

< down, forward(X), turn(90), forward(Y), turn(90), forward(X), turn(90), forward(Y), reset heading vege:- position < < < [100,20,0], text("Vissza karakteres modba? (i/n)"), read token(X), vegez(X). vegez:- clear graphic screen, screenmode < < < text, cut input, ! vegez(n):- clear graphic screen, cut input, !. vegez(X):- cut input, vege. endmod /* szinez /. Programfutás: ? szinez. Jo napot! Segiteni szeretnek szineket valogatni. Milyen legyen a hatter szine? 0. fekete 1. kek 2. zold 3. turkiz 4. piros 5. lila célállítás bejelentkezés 1.kérdés 1.menü Kerem a valasztott szin sorszamat. 8 elsõ válasz Hibas szam. Probalja ujra! b második válasz Hibas szam. Probalja ujra! 4 harmadik válasz Milyen legyen a toll szine? 0. hatter 1. turkiz 2. lila 2. kérdés 2. menü 3. feher Kerem a valasztott szin sorszamat. 3 válasz Vissza karakteres modba? (i/n) 21. ábra A programfutás eredmenye *i Yes 8.l A program célja • A SZÍNEZ

program az elõzõ, a RAJZ program, egy részletének bõvítése. Azt mutatja be, hogyan lehet a programmal való párbeszéd segítségével kényelmesebben és intelligensebben megválasztani a kép hátterének és ábrájának színeit. A felhasználónak nem kell mást tudni, csak elindítani a programot a szinez feladat beütésével, a többi információt menübõl választva, kérdés-felelet formájában közölheti a programmal. 8.2 A feladat feltételrendszete • A kérdés-válasz ideje alatt a képernyõ szöveges üzemmódban kell, hogy legyen. A program bejelentkezése, a felhasználó üdvözlése után kerü sor a háttér és a toll színeinek megválasztására. Ezután a képernyõt át kell váltani grafikus üzemmódba, be kell állítani a szükséges grafikus állapotjelzõket, és ki lehet rajzolni a téglalapot a választott színekkel. A háttér és a toll egy-egy színeit menübõl választhatja ki a felhasználó. A menük felsorolják a

színválasztékot sorszámozva. A sorszámok beütésével lehet a színeket megadni A válaszokat külön eljárás ellenõrzi. Ha a beütött szám nem felel meg egyetlen szín sorszámának sem, vagy esetleg nem is szám, hibajelzést kapunk, és újra lehet próbálkozni. • A program az egyszerûség kedvéért egyetlen téglalap különbözõ színekkel való kirajzolására szolgál. Ha ezt a programot a teljes, RAJZ programmal bõvítenénk ki akkor az adatbázis bármelyik téglalapját kirajzoltathatnánk. 8.3 Menübõl való választás • A számítógéppel való párbeszéd fontos része a felhasználói programoknak. Személyiszámítógépes környezetben meg különösen jogos igény, hogy a felhasználó a program futása közben is megadhasson adatokat, információkat vagy feltehessen kérdéseket a számítógépnek. A számítógéppel való természes nyelvû (magyar, angol, francia stb) társalgás magas szintû megoldása komoly elméleti és gyakorlati

problémákat vet fel, a mesterségesintelligenciakutatás egyik legizgalmasabb területe a természetes nyelv megértése. A PROLOG-ot nagy elõszeretettel alkalmazzák a kutatók ezen a területen. • Jelenleg azonban az alkalmazói programok nagy része olyan kis kérdés-válasz programokkal kezdõdnek, amelyek a menüból való választás módszerét használják a társalgás lebonyolítására. Ez a módszer szinte teljesen kikerüli a természetes nyelv megértésekor felmerülõ problémákat. A gépnek nem kell megértenie a nyelvünket, csak használni kell azokat a válaszokat, amelyeket az általa feltett kérdésekre adtunk. A válaszlehetõségek pedig nagyon korlátozottak, a program mindig megadja elõre a helyes válaszok listáját - ezt hívják menünek. A SZINEZ program két nagyon egyszerû, hasonló szerkezetû menüt használ programhoz szükséges bemenõ adatok bekérésére. Nézzük az elsõt: menu1 :- nl, write("Milyen legyen a hatter szine?"),

nl, write(" 0. fekete "), nl, write(" 1. kek "), nl, write(" 2. zold "), nl, wrrte(" 3. turkiz "), nl, write(" 4. piros "), nl, write(" 5. lila "), nl, write("Kerem a valasztott szin sorszamat."), nl A program elõször feltesz egy kérdést, majd utána azonnal felsorolja a lehetséges helyes válaszokat. A válasz formáját is megszabja. Itt a felhasználónak nincs módja a csevegésre, csak a felsorolt válaszok valamelyikét írhatja be pontosan, különben hibaüzenetet kap. A válasz ellenõrzése a következõképpen történénik: ellenor(N, TOL, IG):read token(N), N is a integer, N > = TOL, N < = IG, cut input, !. ellenor(N, X,Y):- hiba, ellenor(N, X, Y). Az ellenor definíciója két szabályból áll. Az elsõ a beolvasott válasz ellenõrzését végzi , a másodikra akkor kerül a vezérlés, ha hibás a beolvasott válasz. Az elsõ szabály utolsó részfeladataként egy cut szimbólumot,

egy !-et láthatunk Ennek az a szerepe, hogy a két szabályt egymást kizáró értelemben használja a PROLOG következtetési rendszere: Egy beolvasott adat vagy helyes, vagy hibás. Ha egyszer helyesnek bizonyult, egy esetleges visszalépés ne kergesse a programot a hibaágra teljesen jogtalanul. Oda csak akkor kerüljön, ha a válasz ellenõrzésénél az egyik eljárás eredménye hamisnak bizonyulna. • Vegyük sorra az ellenor részfeladatait. A read token beépített eljárás beolvassa azt a szöveget, amit a felhasználó beír, és egyetlen arguiaentumába, amely most a SZIN változó, teszi. Ezek után a SZIN változó értékét minden oldalról ellenõrizni tudjuk Elõször azt vizsgáljuk meg, hogy a SZIN értéke szám-e, ezt a SZIN is a integer beépített eljárás végzi el, majd azt ellenõrizzük, hogy a szám a megfelelõ korlátok között van-e? A cut input beépített eljárás már nem a bemenõ adatok ellenórzéséhez tartozik. Szerepe az, hogy a bemenõ

adatok folyamában letiltsa a visszalépést. Ez azt jelenti, hogy ha a program egyszer beolvasott egy választ, akkor már nem lehet visszatáncolni, és a dolgot meg nem történtté tenni. Még akkor sem, ha a program, ahogy ezt egy tisztességes PROLOG programtól elvárjuk, valamilyen késõbbi kudarc folytán visszalépne. Az ellenor második szabálya a hibaág. Ez a szabály a hibás válasz lenyelését, a hibaüzenet kiírását intézi a hiba részfeladat meghívásával: hiba:- read token(X), cut input, nl, write(" Hibas szam. Probalja ujra!"), nl Ezek után újra meghívja - rekurzív módon - az ellenor részfeladatot, azaz új válaszadással lehet próbálkozni. Újabb hibás válasz esetén a program újra kiírja a hibaüzenetet, és makacsul elvárja a helyes választ. Ebbõl az egyhelyben toprogásból csak úgy lehet kiszállni, ha végre jó választ ütünk be. 8.4 Az írás, olvasás beépített eljárásai • A programmal való párbeszéd csak úgy

valósulhat meg, ha a programozási rendszer rendelkezik olyan beolvasó, kiíró eljárásokkal, amelyekkel a "kérdezz-felelek" kényelmesen megoldható. Minden PROLOG implementáció nagy hangsúlyt fektet az írás, olvasás beépített eljárásainak magas szintû kidolgozására, igy az MPROLOG is. A könyv II Függelékében megtalálható az MPROLOG beépített eljárásainak a listája, közöttük az összes író, olvasó eljárás is. Ebben a fejezetben csak a leggyakrabban használt eljárásokról lesz szó. Az ezeknek megfelelõ beépített predikátumok szinte mindegyik PRO1G implementációban megtalálhatók. • Bemeneti eljárások: read(X) read token(X) read record(Str) read comment(Str) current char code(CC) accept char cut input • beolvas egy kifejezést beolvas egy tokent beolvas egy füzért beolvas egy magyarázatot(/*.*/) a következõ bemenõ karakter kódja CC a következõ beolvasandó karaktert lenyeli a már beolvasott adatfolyamon

letiltja a visszalépést. Kiviteli eljárások: write(X) kiír egy kifejezést write spaces(N) write tab(N) write char code(CC) nl nl(N) new page kiir N szóközt az N-edik karakterre tabulál kiír egy karakter kódot sort emel N sort emel új oldalt kezd ahol X - egy kifejezés N - egy egséz szám CC - egy karakterkód Str - egy füzér Token - egy MPROLOG token A kiviteli eljárások használata olyan természetes, hogy nem is kell hozzá kommentár. A beolvasás azonban a PROLOG-ban már korántsem ilyen egyszerû, ugyanis bonyolítja a helyzetet a program esetleges visszalépése. Szerencsére csak két bemeneti eljárás van ahol a program visszaléphet, a read és a read token eljárások Ezekkel az eljárásokkal ugyanaz a bemenõ adat többször is beolvasható, ha közben a program visszalép, és elfelejti az elõtte történteket. Ez sok esetben nagyon kényelmes lehet, de ugyanolyan sokszor okozhat bajt is Az ebbõl eredõ hibák elkerülésére vezették be a

cut input eljárást, amely egyszerûen letiltja a már beolvasott adatok folyamában a visszalépést. A token lehet • egyetlen írásjel (p1. pont v pontosvesszõ) alfanumerikus (számok vagy betûk) karakterek sorozata speciális karakterek sorozata (kivéve %) idézõjelek közötti karakterek akármilyen hosszú sorozata sor vége jel file vége jel kifejezés vége jel (pont és kocsivissza karakter vagy pont és vesszõ) • A PROLOG kifejezések osztályozását a következõ ábra illusztrálja: 22. ábra PROLOG kifejezések • Tehát egy kifejezés az egy vagy több token, de egy token nem feltétlenül egy kifejezés. A kifejezéseket a read, a tokeneket a read token eljárásokkal lehet beolvasni. A beolvasandó kifejezés végét jelölni kell (pl ponttal) A SZINEZ programban a read token eljárást használtuk cut input-tal kombinálva: ellenor(N , TOL, IG):read token(N), N is a integer, N = > TOL, N < = IG, cut input, !. hiba:- read token(X), cu input, nl,

write(" Hibas szam. Probalja ujra!"), nl 8.5 Írás, olvasás egyik file-ból a másik file-ba • Akkor, amikor írás-olvasásról beszélünk, alapvetõ fontossággal bír, hogy honnan olvasunk és hová írunk. Személyiszámítógépes környezetben az a természetes, ha a program a bemenõ adatokat a billentyûzetrõl kapja (a felhasználó begépeli a szükséges információt), és a képernyõn jelenít meg, tehát a program a billentyûzetrõl olvas és a képernyõre ír. Ez történik általában, és ezért ezt fogadja el a legtöbb PROLOG implementáció alaphelyzetnek A SZINEZ program futtatásakor is ez történik. • Komolyabb felhasználói programok szervezésénél gyakran szükség van arra, hogy a bemenõ adatokat egy külön fileban tároljuk, és innen olvasson be program. Arra is szükség lehet, hogy egy másik file-ba küldje ki a futási eredményeket. Ekkor valamilyen módon közölni kell a programmal, hogy a bemenõ vagy a kimenõ adatok

csatornáját meg szeretnénk változtatni. Az erre alkalmas MPROLOG beépített eljárások: set channel(CH,X) set input(CH) current input(CH) close input(CH) set channel state(CH,X) channel state(CH,X) beállítja a csatorna jellemzõit a bemenõ adatok a CH csatornáról olvasandók a jelenlegi bemeneti csatorna a CH lezárja a bemeneti csatornát beállítja a CH csatorna állapotát X a CH csatorna állapota • Új kimeneti csatorna megnyitását és lezárását egy példával illusztráljuk. Ezt a példát egy nagyobb, építészeti tervezést segítõ PROLOG programból vettük át, amely elõre gyártott elemek alapján lakás alaprajz variánsokat tervez [5]. A megtervezett lakásokat fel szeretnénk használni bemenõ adatként egy másik programhoz, amely többszintes lakóépületeket tervez. Célszerû tehát, a lakástervezõ program kimenõ adatait, az alaprajzi variánsokat, egy új file-ban tárolni. Íme a lagyszerûsített programrészlet: kitesz:nyit(FILE

NEV), kiir(TULAJ), lezar. nyit(FILE NEV) :close output(output), set channel(outfile,"name" = FILE NEV), set output(outfile). lezar :close output(outfile) set output(output). A kitesz definíció három részfeladatot tartalmaz. Az elsõ megnyitja az új kiviteli csatornát A második kiírja az új fileba a lakástervezés eredményeit A harmadik részfeladat lezárja az aktuális kiviteli csatornát, és visszahelyezi alapállapotba a régi kiviteli csatornát. A csatorna megnyitásához és lezárásához néhány olyan beépített eljárást használtunk, amelyek részletes magyarázatától eltekintünk. Az érdeklõdõk utánanézhetnek az MPROLOG kézikönyvben [14] 8.6 Gyakorlatok 1. Bõvítse a programmenüt a következõ kérdéssel: Melyik téglalapot kívánja kirajzolni? (1-6) Ellenõrizze, hogy a válasz helyes-e? Mely definíciók szükségesek a RAJZ programból ahhoz, hogy a kívánt téglalapot lehessen kirajzolni? 2. Módosítsa úgy a menu1 kérdésére

adandó válasz formáját, hogy ne a szín sorszámát, hanem magát a szín nevét kelljen beütni. Hogyan oldaná meg a válasz ellenõrzését? Segítség: készítsen egy adatbázis-definíciót a színekrõl! 3. Milyen baj történne, ha a hiba definícióból elhagynánk a hibás válasz lenyelését, azaz a read token(X) részfeladatot? Hogyan mûködne a program? 9. PROGRAM: LISTA module lista. body. eleme(X, [X¦T]). eleme(X, [Y¦T]):- (X,T). permut([ ],[ ] ). permut(L, [H¦T]):kihagy(H, L, UJLISTA), permut(UJLISTA, T). kihagy(H, [H¦T], T). kihagy(X, [H¦T], [H¦UJLISTA]):kihagy(X, T, UJLISTA). kapcsol([ ],L,L). kapcsol([H¦T],L,[H¦S]):kapcsol(T,L,S). fordit([ ],[ ]). fordit([H¦T],L):fordit(T,R), kapcsol(R,[H],L). endmod /* lista /. Programfutások: ? eleme(c, [a,b,c,d]). Yes c eleme-e a listának? ? eleme(f, [a,b,c,d]). NO f eleme-e a listánák? ? eleme(X, [a,b,c,d]). X=a Continue (y/n) ? y X=b Continue (y/n) ? y X=c Continue (y/n) ? y X=d Continue (y/n) ? y NO

sorold fel a lista összes elemét! ? permut ([a,b,c], L). L = [a,b,c] Continue (y/n) ? y L = [a,c,b] Continue (y/n) ? y L = [b,a,c] Continue (y/n) ? y L = [b,c,a] Continue (y/n) ? y L = [c,a,b] Continue (y/n) ? y L = [c,b,a] Continue (y/n) ? y NO sorold fel a lista összes permutációját! ? kihagy(b, [a,b,c,d], L). L = [a,c,d] Continue (y/n) ? y NO töröljük a b elemet a listából! ? kapcsol([a,b,c], [x,y,z], L). L = [a,b,c,x,y,z] Continue (y/n) ? y NO kapcsoljuk össze a két listát! ? fordit([a,b,c,d], L). L = [d,c,b,a] fordítsuk meg a lista sorrendjét! Continue (y/n) ? y NO 9.1 A program célja • A LISTA program néhány olyan listakezelõ eljárást mutat be, amelyeket jól lehet alkalmazni a PROLOG programozás szinte minden területén. A program segítségével megállapíthatjuk egy lista elemeit, azok permutációit, törölhetünk egy a listából, összekapcsolhatunk két listát és megfordíthatjuk a lista sorrendjét. 9.2 Listák • Még mielõtt

leírnánk a LISTA programhoz tartozó feladatok feltételrendszerét, meg kell ismerkednünk a lista fogalmával, különben érthetetlenek lennének a feltételrendszer kijelentései. A lista egy olyan összetett kifejezés, amely több hasonló dolgot egybegyûjt és sorozatba foglal. A PROLOG rendkívül alkalmas a listák elegáns kezelésére, ez a nyelv egyik kiemelkedõ jó tulajdonsága. Hogyan is néz ki egy lista MPROLOG-ban? Az MPROLOG kétféle listaírásmódot támogat, mindkettõ nemzetközileg elfogadott és gyakran használt konvenció: [a,b,c,d] vagy a.bcdnil A két listának más a külalakja, de egyébként azonos értelmûek egymással. Az elsõ írásmód, amelyet a LISTA programban is használtunk, szögletes zárójelek között felsorolja a lista elemeit, vesszõvel elválasztva. A második írásmód a pont operátorral köti össze a lista elemeit, és nil konstanssal zárja le a listát. A nil önmagában az üres listát jelöli, azaz egy olyan listát,

amelynek nincs eleme. Az elsõ írásmódban ezt a [ ] szimbólummal jelöljük Higgyük el, hogy az üres lista nagyon fontos, legalább annyira, mint az üres halmaz a matematikában, és lépten-nyomon használjuk a PROLOG programozási gyakorlatban! A szögletes zárójeles konvenciót kényelmesebb használni, dg a pont operátoros jelölésmóddal könnyebb megérteni a listák szerkezetét. Ezért a továbbiakban a pont operátoros jelölésmódot alkalmazzuk és késõbb összefoglaljuk, hogy mit hogyan jelölünk a két írásmódban. • Elsõsorban vizsgáljuk meg a pont operátor szerepét. Az operátoroknak általában az a szerepük, hogy egy vagy több kifeiezésbõl összetett kifejezést építsenek fe1. A pont operátor egy úgynevezett bináris infix operátor A bináris azt jelenti, hogy az operátornak két argumentuma van, az infix szóval pedig jelöljük, hogy a pont operátor a két argumentum között (angolul: in) helyezkedik el. Például a.b itt a pont

operátor két konstanst köt össze. Ezt így ábrázolhatnánk: ab • Igen ám, de hogy lesz ebbõl egy akármilyen hosszúságú lista? Például az a.bcdenil listánál hogyan magyarázzuk a pont operátor két argumentumát? A két argumentum szerepe azonnal érthetõ lesz, mihelyt kitesszük az elhagyott zárójeleket: a.(b(c(d(enil)))) Ebben az összetett kifejezésben öt pont operátor szerepel, mindegyiknek két argumentuma van, az elsõ egy elem, a második egy lista. a b c d e (b.(c(d(enil)))) (c.(d(enil))) (d.(enil)) (e.nil) nil A lista tehát tulajdonképpen egy bináris fa, legalábbis a program belsõ ábrázolásában. Ezt tudni kell a program írásakor is, hiszen a listák illesztési szabályait a listák valódi szerkezete szabja meg. Meg kell jegyezni, hogy a fenti bináris fa azért ilyen alakú, mert a pont operátort úgy építették be, hogy a zárójelezés iránya jobbról-balra (rl) legyen. Ha a pont operátor ellenkezõ irányú zárójelezéssel

lenne definiálva (lr), az abcd lista zárójelezett alakja ((a.b)c)d lenne Az operátorok deklarációjáról részletesen a l0 fejezetben olvashatunk Most már, hogy ismerjük a pont operátor szerepét, nem meglepõ, hogy a listát elsõ elemére és maradék listájára, azaz fejére és törzsére szoktuk bontani: a.bcdenil a b.cdenil fej törzs Egy csupa változóból álló listát így is jelölhetünk: F.T vagy FEJTORZS A szögletes zárójeles írásmódban erre külön jelölést vezettek be: [FEJ ¦ TORZS] ahol a fejet és a törzset egy függõleges vonallal (¦ karakter) választják el.Mindkét írásmódban a fej egy elem és a törzs egy lista. • Foglaljuk össze egy tábázatban a listák kétfajta jelölésrendszerét: Szögletes zárójelekkel Pont operátorral [a,b,c,d] a.bcdnil [] nil [a] a.nil [FEJ ¦ TORZS] FEJ.TORZS [X,Y] X.Ynil Megjegyzés négyelemû lista üres lista egyelemû lista legalább egyelemû lista kételemû általános lista A listák

illesztési szabályai a lista szerkezetének ismeretében immár nyilvánvalóak: 1. Egy lista egy változóval illeszthetõ 2. Egy lista egy konstanssal nem illeszthetõ 3. Egy Iista egy másik listával csak akkor illeszthetõ,ha fejeik és törzseik illeszthetõk egymással Például: [a,b,c] és [ ] [a,b,c] és LISTA [a,b,c] és [X¦Y] [a,b,c] és [a] nem illeszthetõ, mert az üres lista egy konstans; illeszthetõ, mert a LISTA egy változó; illeszthetõ, mert X illeszthetõ a-val, Y illeszthetõ [b,c]-vel; nem illeszthetõ, mert a illeszthetõ a-val, de [b,c] nem illeszthetõ [ ]-val. 9.3 Listakezelõ eljárások • A LISTA program több feladat gyûjteménye, az kapcsolja õket össze, hogy mindegyik listákkal kapcsolatos, és mindegyik rekurzív definícióval oldható meg. A következõkben ismertetjük a feladatok megoldásához használt rekurzív definíciókat szavakkal, és mindegyik esetben kiemeljük azokat a szavakat, amelyek a rekurzivitást jelölik. Eleme Egy

kifejezés eleme egy listának, ha a lista feje, vagy ha a lista törzsének eleme. Permutáció Egy lista permutációja azt jelenti, hogy ugyanazokból az elemekbõl más sorrendben épül fel a lista. Az üres lista egyetlen permutációja az üres lista. Ha a lista nem üres, a permutációját úgy állítjuk elõ, hogy kiveszürik belõle egy elemet, a maradék listát permutáljuk, majd a kivett elemet a permutált maradék lista elejére fûzzük. Kihagyás Egy listából kihagyhatunk egy elemet úgy, hogy elhagyjuk a fejét, vagy úgy, hogy kihagyunk egy elemet a törzsbõl. Kapcsol Két lista összekapcsolásával egy új listát kapunk. Az üres lista és egy bármilyen másik lista összekapcsolásából az utóbbi listát kapjuk eredményül. Két bármilyen lista összekapcsolása úgy történik, hogy az elsõ lista törzséhez hozzákapcsoljuk a teljes második listát, az így kapott lista feje - természetesen - az elsõ lista feje lesz. Fordít Az üres lista

megfordítottja az üres lista. Ha a lista nem üres, fejére és törzsére bontjuk, a törzset megfordítjuk, és a megfordított törzset (elsõ lista) összekapcsoljuk a fejbõl mint egyetlen elembõl álló (második) listával. 9.4 Rekurzió listákon • A listakezelõ eljárások rendszerint rekurzív eljárások a PROLOG-ban, hiszen, ha a lista minden elemén kell valamilyen ellenõrzést vagy operációt végrehajtani, természetesen adódik a rekurzivitás. Nézzük meg, hogyan mûködik az eleme eljárás, amely a legegyszerûbb rekurzív definíciók közé tartozik: eleme(X, [X¦T]). eleme(X, [Y¦T]):- (X,T). A definíció azt jelenti, hogy egy kifejezés eleme egy listának, ha a lista feje, vagy ha eleme a lista törzsének. Ennek megfelelõen a definíció két állításból áll. Az elsõ állítás akkor igaz, ha az elsõ argumentumbeli kifejezés a második argumentumbeli lista feje, a második állítás pedig akkor, ha ez a kifejezés a lista törzsének egy

eleme. A rekurzió leállító utasítása az elsõ állítás lesz, a rekurzív újrahívásé pedig a második. • Kövessük nyomon az /* 1 / ? eleme(c, [a,b,c,d]). feladat megoldását! A feladat tehát az, hogy állapítsuk meg, hogy a c konstans eleme-e az [a,b,c,d] listának? Elsõ lépésben a PROLOG következtetési rendszere illeszteni próbálja a célállítást a definíció elsõ állításával: eleme(c, [a,b,c,d]). eleme(X, [X¦T]). 1. feladat 1. állítás Az elsõ argumentumok illeszthetõek, és az illesztés után az X változó felveszi a c konstans értékét. A második argumentumokat már úgy kell nézni, hogy X helyébe c értéket kell gondolni, hiszen ha egy helyen az X változó c értéket kap, akkor a szabályon belül a többi helyen is felveszi automatikusan azt az értéket. Tehát a két lista, amit illeszteni kellene: [a,b,c,d] és [c¦T] Az illesztés nem fog sikerülni, hiszen a két lista feje különbözõ konstans (a és c). Ekkor a PROLOG

interpreter megpróbálja a definíció második állítását: eleme (c, [a,b,c,d]). eleme (X, [Y¦T]):- eleme(X,T). 1. feladat 2. állítás Az illesztés sikerülni fog X = c, Y = a és T = [b,c,d] értékátadásokkal, és mivel a dfiníció második állítása egy szabály, új részfeladatunk lesz: /* 2 / eleme(c, [b,c,d]) Tehát megérkeztünk a rekurzív újrahíváshoz. Az illesztés az elsõ állítással megint nem fog sikerülni: eleme(c, [b,c,d]) eleme(X, [X¦T]) 2. feladat 1. állítás hiszen az X = c értekátadás miatt a két lista feje (b és c) nem illeszkedik. A második állítással viszont sikerül eleme(c, [b,c,d]) eleme(X, [Y;T]):- eleme(X,T). 2. feladat 2. állítás X = c, Y = b, T = [c,d] értékátadásokkal. Az új részfeladat a /* 3 / eleme(c, [c,d]). Ekkor végre sikerül az illesztés a definíció elsõ állításával, eleme(c, [c,d]). eleme(X, [X¦T]). 3. feladat 1. állítás hiszen X illeszthetõ c-vel és T illeszthetõ [d]-vel.

Mivel az elsõ állítás egy tényállítás, nincs feltétel, tehát nincs új részfeladat sem. A feladatot megoldottuk A listát addig fejtettük le rövidebb és rövidebb részlistákra, amíg megtaláltuk azt a listát, amelynek az elsõ eleme a keresett c konstans. • Hogyan történik a hamisra a megállás, azaz mi történik az ? eleme(f, [a,b,c,d]). feladatnál? A program éppen úgy fog mûködni, mint az elsõ esetben, de nem talál olyan részlistát, amelynek elsõ eleme f. Minden egyes rekurzív újrahívásnál fogy a lista, végül teljesen lefogy az üres listára, és ekkor éri a teljes kudarc; az eleme(f, [ ]) részfeladat a definíció egyik állításával sem illeszthetõ! Ennek az az oka, hogy az üres lista ( [ ] ) úgy viselkedik, mint egy konstans, egyetlen más listával sem illeszkedik, csak saját magával. Márpedig az elsõ szabálynál [ ]-t [X¦T]-vel, második szabálynál [ ]-t [Y¦T]-vel kellene illeszteni. Így aztán a PROLOG rendszer

megállapítja, hogy nem tudta a feladatot megoldani, azaz kiírja, hogy NO és leáll. A mi szempontunkból persze minden rendben van, hiszen éppen ilyen választ vártunk a kérdésünkre Az f konstans valóban nem eleme az [a,b,c,d] listának. 9.41 Az argumentumok szerepe • A kihagy és a kapcsol definíciók hasonló szerkezetûek, mint az eleme. Két állítást tartalmaznak, az elsõ a leállító utasításé, a második a rekurzív újrahívásé. Talán annyival bonyolultabbak, hogy predukátumaiknak három argumentumuk van: kihagy(H, [H¦T], T). kihagy(X, [H¦T), [H¦UJLISTA]):kihagy(X, T, UJLISTA). Az elsõ argumentum egy kifejezés, a második kettõ egy-egy lista. Az elsõ argumentumbeli kifejezést kell kihagyni a második argumentumbeli listából, és az eredmény a harmadik argumentumban lévõ lista lesz. • Vizsgáljuk meg a kapcsol definícióját: kapcsol([ ],L,L). kapcsol([H¦T],L, [H¦S]):kapcsol(T,L,S). Itt is három argumentum van. Mindhárom argumentum

lista Az elsõ kettõ összekapcsolásából jön létre a harmadik Ebben a szereposztásban az elsõ két argumentum bemenõ paraméter, a harmadik kimenõ paraméter. De lehet fordítva is használni ezt a definíciót, nem a listák összekapcsolására, hanem felbontására. Ekkor a harmadik argumentum a bemenõ paraméter, és az elsõ kettõ kimenõ paraméter. • Mindkét definícióban fontos szerepe van a listák fejre és törzsre való bontásának. A listákon mûködõ rekurzív definíciók rendkívül tömörek, egy-egy ilyen három soros definíció sok gondolatot testesít meg. Nagyon oda kell figyelni, és többször is át kell tanulmányozni az argumentumokat, hogy megértsük a mögöttük rejlõ információt. Az ilyen típusú definíciókban mindig az a lényeges, hogy a lista fejét és törzsét reprezentáló változó hol szerepel még egyszer a szabályon belül. Ugyanis ez hordozza azt az információt, hogy mi történt a fejére és törzsére bontott

listával Például a kapcsol definíció második szabályában minden változó kétszer szerepel: kapcsol([H¦T],L,[H¦S]):kapcsol(T,L,S). Vegyük sorra. Arról van szó, hogy az elsõ és második listát összekapcsoljuk egy harmadik listává Az elsõ listát felbontjuk fejre (H) és törzsre (T). Az elsõ lista feje: H, az eredménylistának a feje lesz Itt szerepel a H változó másodszor. A T törzshöz viszont kapcsolni kell a második listát A T változó tehát, a rekurzív újrahívás részfeladatában is szerepel. L változóval jelöltük a második kapcsolandó listát, a kapcsol predikátum második argumentumaként. Ez a lista, illetve az õt reprezentáló L változó pontosan olyan formában szerepel a rekurzív újrahívás második argumentumaként is, mint a definíció bal oldalában. Ezt a listát tehát nem boncolgatjuk, az eljáras teljes ideje alatt egyben marad Az újonnan alakuló listát a harmadik argumentumban adjuk meg már eleve felbontott

formában: [H¦S]. A lista feje az elsõ lista feje lesz, a törzsét, az S-et pedig úgy állítjuk elõ, hogy a rekurzív újrahívás segítségével összekapcsoljuk az elsõ lista törzsét a második listával. Itt szerepel az S változó másodszor 9.42 Rekurzión belüli rekurzió • A kihagy és a kapcsol eljárásokat jól lehet használni más rekurzív listakezelõ eljárások megfogalmazásánál. A listák permutációját elõállító eljárásban: permut, a kihagy szerepel részfeladatként, a listafordító eljárás: fordit, pedig a kapcsol eljárást használja. Mivel a permut és a fordit eljárások maguk is rekurzívak, íly módon rekurzión belüli rekurziót kapunk: permut([ ], [ ]). permut(L, [H¦T]):kihagy(H, L, UJLISTA), permut(UJLlSTA, T). fordit([ ],[ ]). fordit([H¦T],L):fordit(T,R), kapcsol(R,[H],L). • A PROLOG-ban akármilyen mélységben lehet egymásba ágyazni a rekurzív definíciókat. Arra nagyon kell vigyázni, hogy csak olyan rekurzív

definíciót használjunk fel más definícióban, amelyet már jól leellenõriztünk, és biztosak vagyunk benne, hogy semmilyen körülmények között nem esik végtelen ciklusba. Ugyanis, ha egy külsõ rekurzív definíció nem úgy mûködik, ahogy azt elvártuk volna, és még abban sem lehetünk biztosak, hogy a belsõ rekurzív definíció tökéletés, akkor nagyon nehéz lesz kideríteni, hogy miért tévedt el a program. 9.5 Gyakorlatok 1. Mi lenne a hatása annak, ha az eleme definíció elsõ szabályába egy cut szimbólumot tennénk? eleme(X, [X¦T]):- !. eleme(X, [Y¦T]):- eleme(X,T). • Hány megoldása lenne az ? eleme(X, [a,b,c]). feladatnak? 2. Szimmetrikus-e a kihagy eljárás, azaz lehet-e veie elemet beszúrni egy listába? Mi lesz az eredménye a következõ feladatnak: ? kihagy(b, L, [x,y,z]). ? 3. Definiálja a diszjunkt listák szabályát! Segítség: két lista akkor diszjunkt, ha nincs közös elemük 4. Olvassa el figyelmesen az alábbi programot,

amely számlisták sorbarendezését végzi el a beszúrásos módszerrel! Írja le szavakkal a definíciókat! rendez ([H¦T], S):- rendez(T, L), beszur(H, L, S). rendez ([ ], [ ]). beszur(X, [H¦T], [H¦L]):- H < X, !, beszur(X, T, L). beszur(X, L,[X¦L]). 5. Miért nem jó az alábbí definíció: kapcsol( [ ], L2, L2). kapcsol( L1,[ ], L1). kapcsol(L1, L2, L1¦L2). 10. PROGRAM: ÉPÜLET module epulet. body. operator( – –,rl,70). operator(+ +,Ir,60). epulet(N):- N is a integer, eloszt(N,L), szerkezet(L,STRUKTURA), abra(N,L), write(STRUKTURA), nl. epulet(N):- N is a integer, N > 12, nl,write("Tul sok lakas! "),nl. eloszt( 12, [4,4,4]). eloszt(11, [3,4,4]). eloszt(10, [2,4,4]). eloszt(9, [2,3,4]). eloszt(8, [0,4,4)). eloszt(8, [1,3,4)). eloszt(8, [2,2,4]). eloszt(7, [1,2,4)). eloszt(7, [0,3,4)). eloszt(6, [0,2,4J). eloszt(6, [2,2,2]). eloszt(5, [0,2,3]). eloszt(4, [0,2,2]). eloszt(4, [0,0,4]). eloszt(3, [0,1,2]). eloszt(3, [0,0,3]). eloszt(2, [0,0,2]).

eloszt(1, [0,0,1 ]). szerkezet([N3,N2,N1], L3+ +L2+ +L1):szint(N1,L1), szint(N2,L2), szint(N3,L3). szint(4, A1– – A2 – – A3 – – A4). szint(3, A1– – A2 – – A3 – – semmi). szint(2, A1– – A2 – – semmi). szint(1, A1– – semmi). szint(0, semmi). abra(N , (N3,N2,N1 ]):nl, write("Az epuletben "), write(N), write(" lakas van."), nl, nl, nyomtat(N3), nyomtat(N2), nyomtat(N1), nl,nl nyomtat(0):- nl. nyomtat(1):- write("X"), nl. nyomtat(2):- write("X X"), nl. nyomtat(3):- write("X X X"), nl. nyomtat(4):- write("X X X X"), nl endmode /* epulet / Programfutások: ? epulet(9). Az epuletben 9 lakas van. XX XXX XXXX 850 – – 851– – semmi + + 846 – – 847 – – 848 – – semmi + + 841– – 842 – – 843 – – 844 Yes ? epulet(8), fail. Az epuletben 8 lakas van. XXXX XXXX semmi + + 847 – – 848 – – 849 – – 850 + + 842 – – 843 – – 844 – – 845 Az

epuletben 8 lakas van. XXX XXXX 851– – semmi + + 847 – – 848 – – 849 – – semmi + + 842 – – 843 – – 844 – – 845 Az epuletben 8 lakas van. XX XX XXXX 850 – – 851 – – semmi + + 847 – – 848 – – semmi + + 842 – – 843 – – 844 – – 845 NO ? epulet(20). Tul sok lakas! Yes ? epulet(valami). NO 10.1 A program célja • Az ÉPÜLET program a "Számítógéppel segített építészeti tervezés" témakörébe enged bepillantást egy eléggé leegyszerûsített példaprogrammal. Az eredeti programrendszer többszintes lakóépületeket tervez adott építészeti sejtek felhasználásával [5]. Ez a kis program a tervezésnek csak egy igen kis szakaszát mutatja be, a lakóház függõleges szerkezetének kialakítását. A program egyetlen bemenõ paramétere a lakások száma Ezeket a lakásokat osztja el a program maximum három szintre, és ezzel kialakítja az épület profilját, amely sok esetben lépcsõzetes,

teraszos. A lakások szintenkénti elosztását egy egyszerû ábrán szemlélteti. A program másii célja a lakóház szerkezetét megadó összetett kifejezés elõállítása, amely bemenõ adatként szolgál egy másik tervezõ programhoz. 10.2 A feladat feltételrendszere • A tervezendõ épületben nem lehet több 12 lakásnál, és legfeljebb 3 szinten, szintenként maximum négy lakás lehet. (Az egy szinten lévõ lakások száma 0-tól 4-ig bármi lehet.) A ház szerkezetét megadó összetett kifejezést úgy kell kialakítani, hogy a lakásokat egy-egy változó jelölje. A változókat úgy kell összekapcsolni, hogy a változók helye a kifejezésben egyértelmûen meghatározza a lakások helyét a házban (melyik emeleten melyik lakás). Ha egy szinten kevesebb, mint négy lakás van, a hiányzó lakások helyét a házat megadó struktúrában konstanssal kelk kitölteni. A lakásokat jelölõ változókat egy másik tervezõprogram fogja feltölteni konkrét

adatokkal, többnyire összetett kifejezésekkel. Ez természetesen mintaillesztéssel történik, éppen ezért kényelmes, hogy a hiányzó részeket konstansokkal töltsük fel, hiszen a konstansok nem illeszthetõk az összetett kifejezésekkel, és így ezekre a helyekre nem kerülnek lakások. A lakóház profilját a sornyomtatón úgy ábrázoljuk, hogy minden lakás helyére egy X karaktert nyomtatunk. 10.3 Összetett kifejezések infix operátorokkal • Ebben a feladatban alapvetõ fontosságú, hogy ügyesen válasszuk meg annak az összetett kifejezésnek a formáját, amely a ház szerkezetét adja meg. Nézzük meg, hogy milyen elgondolások alapján találtunk rá a megfelelõ kifejezésre A ház legnagyobb mérete adott (12 lakás három szinten), induljunk ki ennek a háznak a felépítésébõl: XXXX XXXX XXXX 3. szint 2. szint 1. szint 23. ábra A ház ábrázolása A ház felépítését ezzel a fastruktúrával ábrázolhatjuk: 24. ábra A ház felépítése

Jó lenne egy olyan összetett kifejezést használni, amely ezt a fastruktúrát követi. PROLOG-ban ezt rendkívül kényelmesen meg lehet oldani az infix operátorok használatával. • Legyen a ház struktúrája egy olyan három részbõl álló kifejezés, ahol a kifejezés részeit egy + + operátor köti össze: SZINT3 + + SZINT2 + + SZINT1 A szintek maguk is legyenek összetett kifejezések. Az egy szinten lévõ lakásokat kapcsolja össze egy másik, a – – operátor. SZINT3 = L1– – L2 – – L3 – – L4 Ezek szerint a 23. ábrán lévõ 12 lakásos ház szerkezete így nézne ki: (L1– – L2 – – L3 – – L4)+ + (L5 – – L6 – – L7 – – L8) + + (L9 – – L10 – –L11– – L12) • A zárójeleket a fenti kifejezésben csak úgy lehetne elhagyni, ha valahogy deklarálnánk a programban, hogy a – – operátor erõsebben köti az argumentumait, mint a + + operátor. Továbbá azt is meg kell adnunk, hogy mely irányba történt a

zárójelezés, jobbról balra vagy balról jobbra, mert ez sem mindegy. Éppen erre a célra vezették be az operátordeklarációt. 10.31 Operátordeklaráció • Nézzünk egy példát operátordeklarációra: operátor( – –, rl, 70). Az operátordeklaráció tulajdonképpen egy tényállítás, amelyet a program elejére kell tenni, közvetlenül a body. sora után. Három argumentuma van, az elsõ maga az operátor szimbóluma, a második az operátor fajtája és a harmadik a prioritási száma. Háromfajta operátor létezik: pf prefix sf suffix infix az operátor az egyetlen argumentuma elõtt van; az operátor az egyetlen argumentuma mögött van; az operátornak két argumentuma van az operátorjel két oldalán. Magából az infx operátorból kétféle létezik: rl jobbról balra infix, lr balról jobbra infix. A kétfajta infix operátor között az a különbség, hogy ha több infix operátor is szerepel ugyanabban a kifejezésben, akkor vagy a jobb

végérõl, vagy a bal végérõl kezdjük az operátorok argumentumainak csoportosítását. Nézzünk példákat mindegyik fajta operátorra: Operátor – end . & • Fajta pf sf rl lr Kifejezés –23 file end a.bcnil a&b&c&d Zárójelezett forma –(23) end(file) a.(b(cnil)) ((a&b)&c)&d Az ÉPÜLET programban két infix operátort deklaráltunk: operator( – –, rl, 70). operator( + +, lr, 60). A – – (kétszeres mínusz) operátor irányítása jobbról balra (rl), a + + (kétszeres plusz) operátoré balról jobbra (lr). Hogy lássuk a különbséget a kétfajta zárójelezési irány között, vizsgáljuk meg a következõ két kifejezést részletesen: 1. a – – b – – c – – d 2. a + + b + + c + + d a – – (b – – (c – – d)) ((a + + b) + + c) + + d Rajzoljuk fel mindkét kifejezés bináris fáját, ahol a fa csúcspontjaiban az operátor jelek vannak, minden csúcsból két él indul ki, amelyek végén az oparátor

két argumentuma van. A program írásakor fontos figyelemmel kísérni az összetett kifejezések zárójelezését, mivel a mintaillesztés a zárójelezett forma szerint történik, még akkor is, ha a zárójeleket nem kell mindig kitennünk. Ha egy többszörösen összetett kifejezésben több különbözõ operátor jel fordul elõ, akkor az operátorok prioritási száma is fontos a helyes zárójelezéshez. Például az x++y––z++w x + + (y – – z) + + w kifejezés zárójelezett alakja illetve (x + + (y – – z)) + + w lenne, mivel a – – operátor erõsebben köt, mint a + + operátor. A bináris fája: Fordítsuk meg a példa kedvéért az operátorok prioritását! Legyen operátor ( – –, rl, 70). operátor ( + +, lr, 80). tehát, kössön erõsebben a + + operátor! Ekkor az x + + y – – z + + w kifejezés az (x + + y) – – (z + + w) kifejezéssel egyenlõ. A bináris fája: Nyilvánvaló, hogy ez a bináris fa nem egyenlõ az elõzõvel,

tehát az x + + y – – z + + w kifejezés kétfajta zárójelezése két különbözõ kifejezést ad. 10.32 Az épület kifejezése • Az operátorok deklarációját tárgyaló kitérõ után térjünk vissza eredeti problémánkra, a lakóház szerkezetét megadó összetett kifejezésre. Egy 12 lakásos ház szerkezete tehát a következõ: L1– –L2– –L3– –L4++ L5– –L6– –L7– –L8+ + L9– –L10– –L11– –L12 Az MPROLOG rendszer a változókat a belsõ ábrázolási formájában nyomtatja ki (egy aláhúzást követõ szám, pl. 731) Így a fenti kifejezés a következõképp nyomtatódik ki: 713 – – 714 – – 715 – – 716 + + 717 – – 718 – – 719 – – 720 + + 721 – – 722 – – 723 – – 724 Ez tehát a legnagyobb, a 12 lakásos házat megadó kifejezés. Itt minden szinten 4 lakás van Hogyan jelöljük a csonka szinteket, azokat, ahol egy-két lakás kimarad? Erre a kérdésre a szint definíció

tényállításai adnak választ: szint(4, A1 - - A2 - - A3 - - A4). szint(3, A1 - - A2 - - A3 - - semmi). szint(2, A1 - - A2 - - semmi). szint(1, A1 - - semmi), szint(0, semmi). A szint predikátum két argumentumú, az elsõ argumentum egy szám, amely a szinten lévõ lakások száma, a második pedig a szintet megadó összetett kifejezés. Ha a szinten egyetlen lakás sincs, az üres emeletet a semmi konstans jelöli Ha a szinten egy vagy több lakás hiányzik, a hiányzó lakásokat szintén a semmi konstansal jelöljük. • Kész is vagyunk, ezek után bármely 0 - 12 lakásból álló épülethez tudunk a programmal generálni egy megfelelõ összetett kifejezést. l0.4 Gyakorlatok 1. Illeszthetõ-e az alábbi két összetett kifejezés, ha az operátorok deklarációi a következõk: operator(., rl, 50) operator(#, rl, 60). A.BCnil és X#YT 2. Illeszthetõk-e az l gyakorlat kifejezései, ha a # operátor prioritása kisebb, mint a pont operátoré? A feladat így módosul:

operator(., rl, 50) operator( #, rl, 40). A.BCnil és X#YT 3. Legyen az operátorok deklarációja: operator(., rl, 50) operator(+ +,lr,40). Adja meg a következõ két kifejezés zárójelezett formáit is rajzolja fe1a megfelelõ bináris fákat: A.BC++D és X++Y++ZW 11. PROGRAM: KOCKA module kocka. body. dynamic(szinesor/1). dynamic(szinek/2). forgat :kocka(X), elforgat(X), fail. forgat :szamoz(0), nyomtat. elforgat(HELYZET):egyet fordit(HELYZET, UJHELYZET), megjegyez(HELYZET), elforgat(UJHELYZET). egyet fordit( s(LENT,FENT,ELOL,HATUL,BAL,JOBB), s(LENT,FENT,BAL,JOBB,HATUL,ELOL) ). egyet fordit( s(LENT,FENT,ELOL,HATUL,BAL,JOBB), s(BAL,JOBB,ELOL,HATUL,FENT,LENT) ). megjegyez(X) :szinsor(X), !, fail. megjegyez(X) :add statement(szinsor(X)). szamoz(N) :szinsor(X), M is N + 1, add statement(szinek(X,M)), del nth statement(szinsor/1,1 ), szamoz(M). szamoz(N). nyomtat :szinek(X,N), nl, write(X), write(" "), write(N), nl, del nth statement(szinek/2,1 ), nyomtat. nyomtat.

kocka( s(kek,piros,feher,fekete,sarga,narancs) ). endmod /* kocka /. Progratmfutás: ? forgat. s(kek,piros,sarga,narancs,fekete,feher) 1 s(kek,piros,fekete,feher,narancs,sarga) 2 s(kek,piros,narancs,sarga,feher,fekete) 3 s(kek,piros,feher,fekete,sarga,narancs) 4 s(sarga,narancs,feher,fekete,piros,kek) 5 s(sarga,narancs,piros,kek,fekete,feher) 6 s(sarga,narancs,fekete,feher,kek,piros) 7 s(sarga,narancs,kek,piros,feher,fekete) 8 s(feher,fekete,kek,piros,narancs,sarga) 9 s(feher,fekete,narancs,sarga,piros,kek) 10 s(feher,fekete,piros,kek,sarga,narancs) 11 s(feher,fekete,sarga,narancs,kek,piros) 12 s(piros,kek,narancs,sarga,fekete,feher) 13 s(piros,kek,fekete,feher,sarga,narancs) 14 s(piros,kek,sarga,narancs,feher,fekete) 15 s(piros,kek,feher,fekete,narancs,sarga) 16 s(narancs,sarga,feher,fekete,kek,piros) 17 s(narancs,sarga,kek,piros,fekete,feher) 18 s(narancs,sarga,fekete,feher,piros,kek) 19 s(narancs,sarga,piros,kek,feher,fekete) 20 s(fekete,feher,kek,piros,sarga,narancs) 21

s(fekete,feher,sarga,narancs,piros,kek) 22 s(fekete,feher,piros,kek,narancs,sarga) 23 s(fekete,feher,narancs,sarga,kek,piros) 24 Yes 11.1 A program célja • Adott egy kocka, amelynek minden oldala más színû. A kocka élei a három dimenziós koordináta-rendszer tengelyeivel párhuzamosak. Alaphelyzetben a kocka a vízszintes síkon, a kék oldalán nyugszik, a felsõ lapja piros, a szemközti fehér, a hátsó fekete, a bal oldali sárga és a jobb oldali pedig narancssárga. A program segítségével szeretnénk megkapni a kocka összes lehetséges térbeli állapotát, amelyeket a koordinátatengelyek körüli 90 fokos elforgatások egymás utáni alkalmazásával el lehet érni. 11.2 A feladat feltételrendszere • A kocka térbeli állapotát egy hat argumentumú összetett kifejezéssel reprezentáljuk, ahol a kifejezés argumentumai a színek,a neve pedig s. A kocka alapállapotát például így írjuk le: s(kek,piros,feher,fekete,sarga,narancs). A hat argumaentum rendre

a következõ lapokat jelenti alaphelyzetben: 1.LENT 2.FENT 3.ELÖL 4.HÁTUL 5.BAL 6.JOBB kék piros fehér fekete sárga narancs 25. ábra Egy kocka lapjai Amikor elfordítjuk a kockát, akkor a hat pozícióba más színek kerülnek. Mindhárom koordinátatengely körül végezhetünk 90 fokos elforgatást, de elég két tengely körüli elforgatást használni. Ezek sorozatával elõállítható mind a 24 térbeli állapot (26. ábra) 26. ábra A forgatás irányái Az egyik irány a z tengely körüli elforgatás. A kocka alsó és felsõ lapja nem változik, de a többi igen Például az a lap, amely idáig elöl volt, bal oldalra kerül, a jobb oldaliból lesz az elülsõ lap stb. Az y tengely körüli forgatásnál az elöl és hátul lévõ lapok színei nem változnak, a többi igen. A felül lévõ szín a jobb oldalra, az alul lévõ szín a bal oldalra kerül stb. A kockát egyik helyzetébõl a másikba a fenti két elforgatás sorozatával tudjuk átvinni. • Valahogy

meg kell jegyezni, hogy melyik térbeli helyzet fordult már elõ, nehogy újra ugyanazt a helyzetet állítsuk elõ, és végtelen ciklusba essünk a kocka ismételt egyirányú 90 fokos elfordításával. • A kocka különbözõ térbeli helyzeteit lapjainak színsorával jellemezzük, és ezt is nyomtatjuk ki. 11.3 A forgatás megadása Nézzük meg, hogyan oldottuk meg a kocka forgatását PROLOG programmal: forgat :kocka(X),elforgat(X), fail. forgat :szamoz(0), nyomtat. A forgat célállítás beírásával lehet elindítani a programot. A forgat definíciója két szabályból áll Az elsõ szabály három részfeladatot tartalmaz. A kocka(X)-ben az X változó értéke a kocka alaphelyzete lesz: X = s(kek,piros,feher,fekete,sarga,narancs) és az elforgat(X) részfeladat ebbõl a helyzetbõl forgatja el a kockát. A harmadik részfeladat a fail a rendszer visszalép, és egy másik helyzetet generál új forgatással. Majd megint találkozik a faillel, újra visszalép és

új állapotba forgatja át a kockát. Ez egészen addig tart, amíg az összes lehetõség ki nem merül Ekkor a PROLOG rendszer a forgat definíció második szabályát használva beszámol a kocka összes különbözõ térbeli helyzetét és kinyomtatja. • A kocka elforgatására a következõ rekurzív definíciót használtuk: elforgat(HELYZET) :egyet fordit(HELYZET, UJHELYZET), megjegyez(UJHELYZET), elforgat(UJHELYZET). Ez a definíció egyetlen szabályból ·áll, mégpedig olyanból, amelyik újra meghívja önmagát. Hogyan lehet ebbõl a körbõl kiszállni, mi állítja le azt a ciklust, amelyet ez a rekurzió indít el? Vegyük sorra a részfeladatokat. Az egyet fordit a kocka 90 fokos elforgatását definiálja. Két változata van, az elsõ állítás a z tengely körüli elforgatást, a második az y tengely körüli elforgatást írja le egy-egy tényállítással. Figyeljük meg, hogy milyen egyszerû az elforgatás tényét a PROLOG-ban leírni: a kiinduló

helyzetet reprezentáló összetett kifejezés argumentumainak sorrendje felcserélõdik az elforgatott helyzet kifejezésében, mégpedig éppen úgy, ahogy a kocka lapjainak helyzete változik az elforgatás következtében: egyet fordit( s(LENT,FENT,ELOL,HATUL,BAL,JOBB), s(LENT,FENT,BAL,JOBB,HATUL,ELOL) ). egyet fordit( s(LENT,FENT,ELOL,HATUL,BAL,JOBB), s(BAL,JOBB,ELOL,HATUL,FENT,LENT) ). Az egyet fordit részfeladat mindig sikeres lesz, hiszen mindig úgy hívjuk meg, hogy predikátumának elsõ argumentumában egy összetett kifejezés van, a másikban viszont csak egy változó, és így a mintaillesztés könnyen megy. Feltételek ebben a definícióban nincsenek: Ez a részfeladat tehát nem állíthatja le az elforgat rekurzióját A megjegyez részfeladattal más a helyzet. Ennek az eljárásnak az a feladata, hogy sikersen legyen akkor, ha a kockát egy új, eddig nem látott helyzetbe forgattuk át, ezt az állapotot meg kell jegyeznie (és emlékeznie kell rá késõbb),

és hamisat adjon, ha a kocka elforgatásával egy, már korábbról ismert helyzetet generáltunk, azaz ismételtük önmagunkat. A megjegyez eljárás kudarca visszalépteti a rendszert az egyet fordit részfeladatra, és így a kockát a másik irányba kell, hogy elforgassuk. Ezzel lehet, hogy új helyzetet hoztunk létre, amit a megjegyez sikeresen regisztrál, és tovább forgathatjuk a kockát. Elõbb-utóbb azonban eljutunk odáig, hogy akármelyik irányba is fordítjuk a kockát, már ismert, bejárt helyzetekre találunk. Ekkor a megjegyez sorozatos és végsõ kudarca miatt hamissal kiszállunk a rekurzióból Most nézzük meg a megjegyez definícióját: megjegyez(X) :szinsor(X), !, fail. megjegyez(X) :add statement(szinsor(X)). • Az elõzõ definíció egy új és rendkívül fontos PROLOG programozási eszközt, egy dinamikus beépített eljárást használ az add statement(szinsor(X)) részfeladat meghívásakor. A dinamikus beépített eljárások használata

teljesen megváltoztathatja PROLOG programozási módszereinket, új trükkök lehetõségének egész sorát tárja fel. Ezért érdemes a dinamikus eljárásokról részletesen beszélni. 11.4 Dinamikus beépített eljárások • A PROLOG programozás egyik legizgalmasabb fejezetéhez érkeztünk. Mit jelent az, dinamikus? Magyarul azt jelenti, hogy mozgó, változó. Hát idáig statikus (azaz nem változó, állandó) programjaink voltak? Bármily meglepõ, igen. Példaprogramjaink az 1-tõl a 10-ig statikus programok voltak abban az értelemben, hogy a feladat világát leíró logikai állítások (amelyek magát a PROLOG programot alkotják) a feladat megoldásával nem módosultak, nem bõvültek és nem is csökkentek. A világ állandó volt Ha egy célállítást után lefuttattunk, az eredmény mind a kétszer ugyanaz kellett, hogy legyen. Ez a statikus világkép teljesen összhangban van a predikátum kalkulussal, azaz az elsõrendû matematikai logikával, amire a

PROLOG alapul. De a PROLOG olyan programozási nyelv, amely kilép az elsõrendû logika statikus világából, lehetõséget ad dinamikusan változó, mozgó (pl. fejlõdõ) világok modellezésére is Értsük meg, hogy ezzel óriási lehetõség tárul fel a logikai programozásban. A feladat megoldása közben az új, frissen szerzett ismeretek birtokában módosíthatjuk magát a programot, azaz igazi tanuló programokat lehet írni! Egy program, amely futás közben módosítja önmagát, nyugodtan adhat két teljesen eltérõ eredményt ugyanarra a feladatra. Közben ugyanis eltelt egy kis idõ, okosabb lett, azaz bõvültek az ismeretei a világról. És ehhez az önmagában is hallatlanul erõs eszközhöz a visszalépés lehetõsége kever némi pikáns ízt. A világot megváltoztathatjuk menet közben, de ha valamiért nem tetszik, visszatáncolhatunk, mintha mi sem történt volna! Ez olyan gyönyörû, hogy csak sajnálhatjuk, amiért a valódi világ nem visszalépéssel

mûködik. • Most nézzük meg részletesen, hogy többek között milyen funkciókra lehet szükség a dinamikus (változó) világképhez: 1. Futás közben szeretnénk bõvíteni a programot egy új PROLOG állítással, amely visszalépés esetén is érvényes marad. 2. Futás közben szeretnénk bõvíteni a programot egy új PROLOG állítással, amely visszalépés esetén érvényét veszíti, mintha ott sem lett volna. 3. Futás közben törölni szeretnénk a programból egy PROLOG állítást, amely visszalépés esetén is hiányozni fog a programból. 4. Futás közben törölni szeretnénk a programból egy PROLOG állítást, amely visszalépés hatására feltámad, és újra érvényes lesz. A legtöbb PROLOG implementáció csak az 1. és 3 funkciót valósította meg beépített eljárásokkal, azaz azokat, amelyek hatása visszalépés esetén nem változik. Az MPROLOG azonban mind a négy funkcióra ad beépített eljárást: 1. 2. 3. 4. add statement(S) add

statement b(S) del statement(S) del statement b(S) felveszi az S állítást felveszi az S állítást visszaléptethetõen törli az elsõ S-sel illeszthetõ állítást törli az elsõ S-sel illeszthetõ állítást visszaléptethetõen A többi dinamikus eljárás ennek a négy funkciónak a finomítása,variálása.A legfontosabbak közülük: 5. add statement(S,X) 6. add statement b(S,X) felveszi az S állítást az X pozícióra visszaléptethetõ változat amelyekben az S egy PROLOG állítás, az X pedig a következõ pozíciók valamelyike: bottom top before N after N ascend N descend N S lesz az utolsó állítás S lesz az elsõ állítás S lesz az N-edik állítás S lesz az N +1-edik állítás S-et beszúrja a második argumentum szerinti növekvõ sorrendben S-et beszúrja a második argumentum szerinti csökkenõ sorrendben 7. del all statements(D) 8. del all statements b(D) 9. del nth statement(D,N) 10. del nth statement b(D,N) törli a D definíció összes

állítását visszaléptethetõ változat a D definíció N-edik állítását törli visszaléptethetõ változat ahol N egy egész szám és D a leíró (angolul designator) leíró = predikátumnév / argumentumok száma (Pl: szinsor/1) Az MPROLOG rendszerben azokat az állításokat, amelyeket dinamikusan szeretnénk használni, deklarálni kell a modul elején. Ez sok PROLOG rendszerben nem szükséges Az MPROLOG-ban a deklaráció megadása: dynamic(szinsor/1). amelyben a szinsor/1 a szinsor állítás leírója. 11.5 A végtelen ciklusok elkerülése • Most vegyük szemügyre még egyszer a megjegyez eljárást, most már érthetõ lesz, hogyan is mûködik, hiszen a benne kulcsfontosságú szerepet játszó add statement dinamikus eljárást már bemutattuk. megjegyez(X) :szinsor(X), !, fail. megjegyez(X) :add statement(szinsor(X)). Kezdjük az elején, a kocka legyen alaphelyzetben. Az elforgat részfeladat meghívásakor egyet fordítunk a kockán (mégpedig a z

tengely körül 90 fokkal). Az új állapot az UJHELYZET változóba kerül: UJHELYZET = s(kek,piros,sarga,narancs,fekete,feher) és ezzel az összetett kifejezéssel hívjuk meg a megjegyez(UJHELYZET) részfeladatot. A megjegyez definíciója két állításból áll, elõször természetesen az elsõvel próbálkozunk A mintaillesztés következtében az X változó felveszi az UJHELYZET-ben lévõ összetett kifejezést értékül, és így a szinsor(X) részfeladatot ezzel az értékkel hívjuk meg: szinsor(s(kek,piroi,sarga,narancs,fekete,feher)). De szinsor nevû tényállítás a programunkban nem is létezik, úgyhogy ez a részfeladat kudarcot vall. Ekkor következik a második megjegyez állítás Ennek a szabálynak csak egy részfeladata van, amely az add statement dinamikus eljárás alkalmazásával felveszi a programba a szinsor(X) tényállítást, és ennek az X változójába kerül az UJHELYZET értéke. Tehát, a programunk bõvül egy

szinsor(s(kek,piros,sarga,narancs,fekete,feher)). tényállítással. • A megjegyez részfeladatot sikerült megoldanunk. Ekkor az elforgat szabályt újra meghívjuk, de most már új helyzetbõl kell elforgatni a kockát. Egyet fordítunk, ismét a z tengely körül, és az UJHELYZET argumentumába az s(kek,piros,fekete,feher,narancs,sarga) kifejezés kerül. A megjegyez elsõ szabályában lévõ szinsor(X) részfeladat most azt fogja ellenõrizni, hogy létezik-e a programban olyan szinsor tényállítás, amelyben az egyetlen argumentum értéke az elõzõ kifejezés. Ilyen egyelõre még nincs, és ezért a megjegyez elsõ szabályából újra hamissal szállunk ki. A második szabály viszont felvesz egy olyan új szinsor(X) tényállítást a programhoz, amelyben az X értéke a kocka új elforgatott állapotát. Most már két szinsor tényállításunk van. A kocka elforgatása tovább folytatódik a z tengely körül A negyedik elforgatás után visszakapjuk a kocka

eredeti állapotát, és ezt is felvesszük a szinsor tényállítások közé. Ekkor már négy lesz Ötödszörre újra a z tengely körül forgatunk, de ezzel olyan állapotba forgatjuk a kockát amely már elõfordult egyszer, és a megjegyez emlékszik is rá. Az volt az elsõ olyan állapot, amelyet rögzített a szinsor tényállítás segítségével Most fog elõször elõfordulni, hogy a megjegyez elsõ szabályában lévõ szinsor részfeladat sikeresnek bizonyul, és így sor kerül a további részfeladatok megoldására. Ezek egy cut (!) és egy fail A cut letiltja az esetleges késõbbi visszalépésnél egy másik alternatíva választását a megjegyez belsejében, a fail pedig azonnal hamisat ad, és visszalépést okoz. A megjegyez részfeladatot nem tudjuk megoldani, mivel a második szabály alkalmazása a cut miatt tilos. Ez további visszalépést okoz, amellyel kibújunk a megjegyez belsejében lévõ cut hatáskörébõl. Az egyet fordit részfeladat új

lehetõségére kerül sor, azaz most elõször fogjuk a kockát az y tengely körül elforgatni. A megjegyez részfeladat tehát megakadályozza, hogy örökké a z tengely körül forgassuk a kockát, azaz elkerültük a végtelen ciklus veszélyét. Összefoglalva, két eset lehetséges: 1. Ha a kocka új állapota még nem fordult elõ, akkor a rendszer felveszi új állításként a programba, és a kocka tovább forgatható a z tengely körül. 2. Ha a kocka új állapota már létezik a megjegyzett állapotok között, akkor a rendszer visszalép, elfelejti az utolsó állapotot, és az y tengely körüli forgatással új mederbe tereli a feladat megoldását. 11.6 Dinamikus definíciók • A dinamikus beépített eljárások segítségével új definíciókkal bõvíthetjük a programunkat úgy, ahogy ezt a szinsor esetében tettük. Ezek a definíciók élnek a programban, mûködnek, hatásuk van, még akkor is, ha elfelejtkezünk, róluk, mivel láthatatlanok. A

programozási gyakorlat azt mutatja, hogy a dinamikus definícókkal nagyon kell vigyázni, csak addig szabad életben tartani õket, amig feltétlenül kell. Ha a feladat nem kívánja másképp, ajánlatos a programot úgy szervezni, hogy az a létrejött dinamikus definíciókat törölje, mielõtt a program Yes-szel vagy No-val leáll. Így egy új célállítás futtatásánál tiszta lappal indulhatunk. • Nagyon hasznos, és gyakran alkalmazható funkciót tölt be a szamoz eljárás; sorszámozza a dinamikus definíciókat. Éppen azért, mivel a program generálja futás közben, a programozónak gyakran fogalma sincs arról, hogy egy dinamikus defincíó hány állításból áll. Márpedig ezt néha jó tudni A szamoz definíció a következõképpen oldotta meg ezt a feladatot: szamoz(N) :szinsor(X), M is N + 1, add statement(szinek(X,M)), del nth statement(szinsor/1,1 ), szamoz(M). szamoz(N). A szamoz rekurzív definíció, amely egy 0-tól kezdõdõ, egyesével haladó

ciklust generál. A forgat definíció hívja meg, szamoz(0) formában. Elsõ állítása a rekurzív újrahívást, a második a rekurzió leállítását oldja meg A beszámozás úgy történik, hogy a rendszer a szinsor(X) részfeladattal megkeresi az elsõ szinsor állítást, eggyel növeli a sorszámot (elõször 0 + 1 = l lesz), majd felvesz egy új állítást, amelynek neve szinek. A szinek dinamikus tényállítás, predikátumának két argumentuma van. Az elsõ a kocka térbeli állapota, amelyet a szinsor(X) egyetlen argumentuma tartalmazott, a második az éppen aktuális sorszám. Ezek után, a program nyugodtan kihúzhatja a szinsor állîtást, hiszen a benne lévõ információ most már a szinek állításba került át, ráadásul sorszámmal ellátva. Amikor a szamoz(N) meghívja önmagát, argumentumának értéke, amely egy egész szám, eggyel nagyobb lesz. A szinsor(X) részfeladat most az eredetileg második állítással fog illeszkedni, mivel az elsõt már

kihúztuk, és az megszünt létezni. Ehhez is felveszünk egy új szinek állítást, amely most már a 2 sorszámmal lesz ellátva És így tovább egészen addig, amíg a szinsor definícióban van még állítás. Ha elfogy, a szinsor(X) részfeladatot már nem tudjuk megoldani, ezért ebbõl a szabályból hamissal kiszállunk, és a szamoz második állításával leállítjuk a rekurziót. • A nyomtat definíció szerkezete nagyon hasonlít a szamoz definíciójához. Szintén rekurzív, és a funkciója az, hogy állításonként kinyomtassa és kihúzza a szinek dinamikus definíciót: nyomtat :szinek(X,N), nl, write(X), write(" "), write(N), nl, del nth statement(szinek/2,1 ), nyomtat. nyomtat. A programozási gyakorlat azt mutatja, hogy kezdõ PROLOG.programozók nagyon szívesen és gyakran használják a dinamikus eljárásokat, ha kell, ha nem. Gyakorlottabb PROLOG programozók már tudják, hogy milyen fölösleges bonyodalmakat okozhat a dinamikus

eljárások indokolatlan használata, amelyek ráadásul lassúak, és nagyon terhelik a PROLOG rendszert. Egy PROLOG program attól csak jobb lesz, ha nem tartalmaz fölösleges dinamikus eljárást, amely kiváltható más eszközökkel. 11.7 Gyakorlatok 1. Az egyet fordit definíció a z és az y tengely körüli 90 fokos elforgatást definiálja Hogyan lehetne az x tengely körüli elforgatást leírni? 2. Hogyan lehetne a szinek dinamikus definíció kinyomtatását megoldani rekurzió nélkül? Segítség: gondoljon a visszalépés adta lehetõségekre! 3. Tudjuk, hogy a hat színnek sokkal több permutációja van (6! = 1 * 2 3 4 5 6 = 720), mint ahány elforgatott helyzete a hatszínû kockának (24). Írjon egy olyan eljárást, amely azt ellenõrzi, hogy a hat szín egy adott permutációja megfelel-e a kocka egyik elforgatott helyzetének! l2. PROGRAM: MODULOK module main. import(metszet/2, teglalap/2, hatter/1, toll/1, kezdet/2, teglalap vonala/4, vege/0). body.

program:ket teglalap(2,3). ket teglalap(R1,R2) :screenmodee < < < text, metszet(R1,R2),!, kerdez(X), hatter(C), toll(PC), kezdet(C,PC), rajzol(R1), rajzol(R2), vege. /* mod. teglalap*/ /* mod. szinez */ /* mod. szinez */ /* mod. szinez */ kerdez(X):nl, nl, write("Szeretne latni a kepet? (i/n)"), nl, read token(X), cut input, valasz(X). valasz(i). valasz(n):- nl, write("VISZONTLATASRA!"), nl, fail. rajzol(N):teglalap(N, r(p(X1,Y1),p(X2,Y2))), Z1 is X1*40, W1 is (Y140)+100, Z2 is X240, Z2 is (Y2*40) + 100, X is Z2-Z1, Y is W2-W1, teglalap vonala(Z1,W1,X,Y). :- program. endmod /* main /. module teglalap. export(metszet/2, teglalap/2). body. metszet(A, B) :teglalap(A,R1), teglalap(B,R2), metsz(R1,R2,R3), /* teglalap / /* mod. szinez */ nl, write(R3). metszet(A,B) :nl, write("Diszjunktak.") metsz(r(p(X1,Y1),p(X2,Y2)), r(p(K1,L1),p(K2,L2)), r(p(M1,N1 ),p(M2,N2))) :szamol(X1,X2,K1,K2,M1,M2), szamol(Y1,Y2,L1,L2,N1,N2). szamol(A,B,C,D,X,Y)

:max(A,C,X), min(B,D,Y), min(X,Y,X). min(X,Y,X) :- X < Y. min(X,Y,Y). max(X,Y,X) :- X > Y. max(X,Y,Y). teglalap(1,r(p(4,2,p(8,6))). teglalap(2,r(p(6,10),p(10,15))). teglalap(3,r(p(7,8),p(15,12))). teglalap(4,r(p(13,4,p(18,14))). teglalap(5,r(p(5,3,p(6,5))). teglalap(6,r(p(15,3,p(18,8))). endmod /* teglalap /. module szinez. export(hatter/1 , toll/1, kezdet/2, teglalap vonala/4, vege/0). body szinez:screenmode < < < text, nl, write("Jo napot! Segiteni szeretnek szineket "), write("valogatni."), nl, hatter(C), toll(PC), kezdet(C,PC), teglalap vonala(300,200,260,180), vege. hatter(C):- menu1, ellenor(C,0,5). toll(PC):- menu2, ellenor(PC,0,3). menu1:- nl, write("A hatter szinvalaszteka: "), nl, write(" 0. fekete "), nl, write(" 1. kek "), nl, write(" 2. zold "), nl, write(" 3. turkiz "), nl, write(" 4. piros "), nl, write(" 5. lila "); nl, nl, write("Kerem a valasztott szin

sorszamat:"), nl. menu2 :- nl, write("A toll szinvalaszteka: "), nl, write(" 0. hatter "), nl, write(" 1. turkiz "), nl, write(" 2. lila "), nl, write(" 3. feher "), nl, nl, write("Kerem a valasztott szin sorszamat."), nl ellenor(N, TOL, IG):read token(N), N is a integer, N > = TOL, N < = IG, cut input, !. ellenor(N, X, Y):- hiba, ellenor(N, X, Y). hiba:- read token(X), cut input, nl, write (" Hibas szam. Probalja ujra!"), nl kezdet(C,PC):screenmode < < < graphic, reset, reset heading, color < < < C, pencolor < < < PC. teglalap vonala(Z1,W1,X,Y):position < < < [Z1,W1,0], pen < < < down, forward(X), turn(90), forward(Y), turn(90), forward(X), turn(90), forward(Y), reset heading. vege:- position < < < [100,20,0], , text("Vissza karakteres modba? (i/n)"), read token(X), vegez(X). vegez(i):- clear graphic screen, screenmode < < <

text, cut input, !. vegez(n):- clear graphic creen, cut input, !. vegez(X):- cut input, vege. endmod /* szinez /. Programfutás: ? program. r(p(7,10),p(10,12)) Szeretne latni kepet? (i/n) i A hatter szinvalaszteka 0. fekete 1. kek 2. zold 3. turkiz 4. piros 5. lila Kerem a valasztott szin sorszamat. 4 A toll szinvalaszteka: 0. hatter 1. turkiz 2. lila 3. feher Kerem a valasztott szin sorszamat. 1 Visszateres karakteres modba ? (i/n) 27. ábra A programfutás eredménye 12.1 A program célja • A MODULOK program a már ismertetett TÉGLALAP és SZÍNEZ programok összekapcsolását mutatja be. Kiszámolja két téglalap metszetét, majd a menükbõl kiválasztott színekkel kirajzolja a két téglalapot. 12.2 A feladat feltételrendszere • Ez a program három modulból áll. A main, a fõmodul a két másik - a teglalap és a szinez - modul néhány definícióját használja. Maga a fõmodul nagyon egyszerû, gyakorlatilag nem tartalmaz mást, mint a már ismert eljárások

meghívásainak megszervezését. A feladat feltételrendszere így a TÉGLALAP és a SZÍNEZ programok feltételrendszereibõl áll össze, amelyeket már leírtunk a 6. és a 8 fejezetben 12.3 Az MPROLOG rendszer részei • Ahhoz, értsük egy MPROLOG program modulszerkezetét, és a modulok kapcsolati rendszerét, egy kicsit meg kell ismerkedni magának az MPROLOG rendszernek a felépítésével is. • Az MPROLOG rendszer, amelyet az angol irodalomban LOGIC-LAB-nek is hívnak, öt különálló egységbõl áll: 1. Intetakív programfejlesztõ alrendszer (PDSS, angolul Program Development Support System) az MPROLOG programok írására, módosítására, tesztelésére. 2. Elõfordító vagy pretranszlátor, amely lefordítja az MPROLOG forrásmodulokat bináris formába 3. Fordítóprogram, amely a bináris modulokat gépi kódba fordítja 4. Modulösszefûzõ program vagy konszolidátor, amely összekapcsolja az elõfordított és modulokat gépi kódú önálló programokká.

5. Értelmezõprogram vagy interpreter, amely futtatja az önálló programokat A következõ ábra mutatja a különbözõ egységek összefüggéseit (28. ábra) A szaggatott vonal azt jelenti, hogy a fordítóprogram nem mindegyik megvalósításnak része. Az PC változat például jelenleg nem tartalmazza a fordítóprogram egységeit. 28. ábra Az MPROLOG rendszer részei Ha a fordítóprogram egységét figyelmen kívül hagyjuk, az MPROLOG programok futtatásának kétféle formáját különböztethetjük meg: 1. A PDSS interaktív üzemmódja interaktív kömyezetet nyújt az MPROLOG programok fejlesztéséhez és teszteléséhez. A PDSS környezetben fejlesztett és tárolt MPROLOG programokat közvetlenül az interpreter segítségével lehet futtatni. Ez az az interaktív üzemmód, amelyet a programok futtatásánál használtunk, és amelyet a könyvben végig feltételeztünk. 2. Önálló (stand-alone) programok futtatása, az MPROLOG programok futtatási módja Erre

akkor kerül sor, ha a programfejlesztés szakaszának vége, a program már tökéletes, el akarjuk adni, és ezért az forrásprogramokból ún. önálló programokat állítunk elõ. Ez azért célszerû, mert egyrészt a programok üzemszerû futtatásához nem kell a PDSS számos kényelmi eszköze, amelyek a program fejlesztését könnyítették, tehát helyet és idõt nyerünk, másrészt pedig biztonságosabb olyan formában eladni a programot, hogy az ne legyen könnyen megfejthetõ és módosítható. Az önálló programok elõállításához használjuk az elõfordítót (esetleg a fordítót) és a modulösszefûzõ programot. Az önálló programok elóállításának a szabályai megtalálhatók a "Logic-Lab Reference" c. kézikönyvben [15], ebben a könyvben nem kívánunk vele részletesen foglalkozni. • A modul kapcsolatokat a PDSS igen rugalmasan kezeli. Sok szabalytalanságra nem ad hibajelzést De ha a hibás programot megkapja az elõfordító, az

már szigorúan ellenõrzi a modul kapcsolatokat, és csak akkor engedi át a programot a modul összefûzõegységnek, ha hibátlan. 12.4 Moduláris programozás • Ebben a könyvben csak nagyon egyszerû és rövid programokat közlünk, hiszen a PROLOG alapjait csak így lehet megismertetni az Olvasóval. Mindenki számára nyilvánvaló azonban, hogy komoly mérnöki alkalmazói programok, vagy a szakértõi rendszerek sokrétû és bonyolult problémákat oldanak meg, és ezért a programok is jóval hosszabbak és bonyolultabbak. Hogy a nagy programok is áttekinthetõek legyenek, a modern programozási elvek a hierarchikus rendszerépítés módszerét javasolják a programozási gyakorlatba átültetni. A hierarchikus programszerkezetnek az a lényege, hogy a program jól elkülöníthetõ egységekbõl áll, nevezzük õket moduloknak, és a program modulok egymáshoz meghatározott szabályok szerint kapcsolódnak. A moduláris programozás azért is rendkívül kellemes

nagy feladatok megoldásánál, mert egyszerre több programozó is dolgozhat a különbözõ részfeladatokon (modulokon), és csak a modulkapcsolatokat kell közösen specifikálni. Célszerû a modulszerkezetet piramisszerûnek választani, ami azt jelenti, hogy van egy fõmodul a legfelsõ szinten, ez hívja meg az eggyel lejjebbi szinten lévõ modulok eljárásait, és ezek a modulok maguk is hívhatnak a még lejjebbi szinten lévõ eljárásai közül (29. ábra) 29. ábra Hierarchikus programszerkezet Az MPROLOG megvalósítása ezt a moduláris, hierarchikus programszerkesztést nagy mértékiben elõsegíti. Egy MPROLOG program egy vagy több modulból áll. A modulok közötti interakciót a modulkapcsolatok irányítják, amelyek részei a moduloknak. Az egyik modul állítása csak akkor fogja egy másik modul futását befolyásolni, ha az a modulkapcsolatban specifkálva van. Egy modul szerkezete a legáltalánosabb esetben: module < modulnév >. [ < modul

kommentár > ] [ < modulkapcsolat > ) body. [ < deklarációk > ] [ < definíciók > ] [ < modul célállitás > ] endmod. A < > zárójelek közötti szöveg a megfelelõ konkrét szintaktikus egységek helyét jelöli ki a modulban. A [ ] zárójelpár azt jelenti, hogy a benne lévõ programrészlet elófordulása opcionális, azaz megadása nem kötelezõ. A modul nevét ki kell tenni. Két modult nem hívhatunk ugyanazzal a névvel A modul magyarázat (vagy kommentár) nem lehet hosszabb egy sornál, formája: /* < kommentár > / Használata nem kötelezõ. A kommentárok a programozónak nyújtanak segítséget, a program futását semmilyen módon sem befolyásolják. A modulkapcsolat a többi modullal való kapcsolatot definiálja. Ha a modul teljesen önálló, a modulkapcsolat nem tartalmaz egyetlen kapcsolatmegadást sem. Ellenkezõ esetben az alábbi kapcsolatmegadások fordulhatnak elõ a modulkapcsolatban: all global. all symbolic.

all visible. global( < név >, .) symbolic( < név >, .) visible( < név >, .) local( < név >, .) coded( < név >, .) hidden( < név >, .) export( < név > [/ < egész szám > ), .) import( < név > [/ < egész szám > ], .) Az elõzõ kapcsolatmegadásokról a 12.5 alfejezetben részletesen fogunk beszélni • A modulnév és a body közötti sorokat a modul fejének nevezzük. A modul feje tehát a kommentárokat és a modulkapcsolatot tartalmazza. • A body és az endmod közötti sorokat a modul törzsének nevezzük. A modultörzs elején vannak a deklarációk, ha vannak. A deklarációknak négy fajtája van, ezek közül kettõvel már találkoztunk A deklarációk formája a következõ: operator( < név >, < név >, < név > ). dynamic( < név > / < egész szám > ). external( < név > / < egész szám >, < füzér > ). value( < név > ). • Az operator

deklarációkkal a 10. fejezetben foglalkoztunk A dynamic deklaráció azt jelöli ki, hogy melyik predikátumot fogjuk dinamikusan kezelni, errõl már a 11. fejezetben írtunk Az external egy másik nyelven (pl assembler nyelven) megírt eljárások használatát deklarálja, a value pedig azt, hogy mely neveket fogjuk az értékek tárolására használni. Ez utóbbi két deklarációval nem kívánunk részletesebben foglalkozni • A definíciók fajtáit és formáit már bemutattuk. • Célállítást is meg lehet adni magában a modulban. Ez egy olyan lehetõség, amelyet az eddigiek során nem használtunk ki, mivel az MPROLOG interaktív üzemmódjának a használatát feltételeztük, azaz a PDSS-t használtuk. Az önálló (stand-alone) programok futtatásánál azonban elõfordulhat, hogy a betöltés után azonnal futtatni akarjuk a programot, minden interaktív beavatkozás nélkül. Ekkor a célállítást a programban kell megadni A több modulból álló MPROLOG

program futását a fõmodulban lévõ célállítás indítja el. A MODULOK programunkban a fõmodul (main) célállítása: :- program. Ha a programot PDSS-ben futtatjuk, akkor a program a ? program. célállítás begépelésével és elküldésével indítható el, de elég a run parancs is, hiszen a PDSS tudja, hogy mi a cél. l2.5 Modulkapcsolatok 12.51 Lokális és globális nevek • A különbözõ modulok kapcsolatát a modulokban használt neveken keresztül lehet a legrészletesebben meghatározni. A nevek lehetnek predikátumnevek, struktúranevek, konstansok nevei. Minden névre specifkálni lehet, hogy egy név látható-e egy másik modul számára, vagy lokális a moduljára nézve. Ezen kívül a neveknek az MPROLOG-ban két fajtája lehet, szimbolikus egy név, ha a formája olyan, ahogy az a programban le van írva, és kódolt, ha le van fordítva egy belsõ, tömörített formára. Ha egy nevet két különbözõ modulban használunk, és rmind a kettõben

láthatónak (visible) deklaráljuk, akkor az MPROLOG ezt ugyanazon név két különbözó elõfordulásának fogja értelmezni. Ha az egyik modulban nincs láthatónak deklarálva, akkor az MPROLOG úgy fogja a két név két elõfordulását kezelni, mint két teljesen különbözõ nevet. Az MPROLOG normálisan a neveket egy belsõ, tömör alakban, kódolt formában tárolja. Vannak azonban olyan esetek, amikor a név meg kell hogy õizze az eredeti, a programozó által meghatározott (szimbólikus) formáját. Ezek a füzérkezelés, a beolvasás és a kiírás esetei. Ilyenkor a neveket szimbolikusnak kell deklarálni az all symbol vagy a symbolic ( < név1 > , < név2> , .) kapcsolatmegadással Ha egy név látható és szimbolikus is, akkor globális névnek hívjuk. Az all global az összes nevet egy csapásra láthatóvá és szimbolikussá teszi, a global ( <név1> , <név2>, ) pedig ugyanezt teszi a zárójelben felsorolt nevekre.

Alapértelmezésben tehát ha semmit nem deklarálunk, minden név lokális a modulban, azaz a többi modul részére nem látható. Ha a modulkapcsolat az all visible specifikációt tartalmazza, akkor ez azt jelenti, hogy a modulban lévõ összes nevet láthatóvá akarjuk tenni a többi modul részére. Ha csak néhány nevet szeretnénk láthatónak deklarálni, a visible( < név1 >, < név2 >, .) kapcsolatmegadást használnunk A visible, symbolic, global kapcsolatmegadások ellenpontja a local, a coded és a hidden (lokális, kódolt, rejtett), amelyek formája teljesen analóg a fentiekkel. Tehát, ami lokális és kódolt az rejtett • Foglaljuk össze új ismereteinket egy táblázatban: 30. ábra Nevek specifikációja Alapértelmezésben: 1. A beépített eljárások nevei globálisak 2. Az exportált, importált predikátumok (1 a következõ szakasz) nevei láthatóak 3. Minden modul neve globális a programban 4. Az összes többi név rejtett 12.52

Exportált, importált predikátumok • Az export, import specifikációkat a predikátumnevekre használhatjuk. Importáljuk a predikátumot, ha nem az adott modulban definiáltuk, ahol használjuk, illetve exportáljuk, ha az adott modulban definiáljuk, de egy másikban is akarjuk használni. Tehát, hogyha egy predikátumot több modulban is használni akarunk, akkor exportáljuk abból a modulból, ahol definiálva van, és imprtáljuk minden egyes modulba, ahol használni akarjuk. Az export, import kapcsolatmegadások formája igen egyszerû, meg kell adni a predikátum nevét és argumentumainak számát. Például: import (metszet/2, hátter/1, vege/0). export (teglalap vonala/4). • Az export, import specifikációk helyett használhatjuk a visible specifikációt. Az eredmény ugyanaz lesz, hiszen a predikátumneveket egyszerûen láthatóvá kell tenni ahhoz, hogy több modulban is használhassuk õket. Mi mégis az export, import specifkációkat használjuk a MODULOK

programban, mivel ezzel a módszerrel sokkal érthetõbbé válik a program hierarchikus felépítése. 12.6 Gyakorlatok 1. Tegyük fel, hogy két programozó írt egy-egy modult, amelyekben mindketten használták a nulla argumentumú alma predikátumnevet, és az egy argumentum barack predikátumnevet. Késõbb kiderült, hogy a két modult össze kell kapcsolni, mégpedig úgy, hogy az alma eljárás az elsõ modulban legyen meghívva, és a második modulban definiálva. A barack predikátumnév viszont két teljesen különbözõ eljárást jelölt a két modulban, amelyek között semmi kapcsolat nem volt. Hogyan kell megadni a modulkapcsolatokat? 2. Hiba-e, ha egy olyan predikátumnevet exportálunk, amely nincs definiálva a modulban? 3. Hiba-e, ha egy predikátumnevet exportálunk is, meg importálunk is ugyanabban a modulban? UTÓSZÓ • Remélem, hogy az Olvasó, aki figyelmesen nyomon követte a tizenkét programhoz fûzött magyarázatokat, immár el tudja dönteni, hogy

megtetszett-e neki a PROLOG programozás, lát-e fantáziát benne, és az õt foglalkoztató feladatot szeretné-e PROLOG-ban megoldani, avagy sem. Tulajdonképpen ez volt a célja ennek a könyvnek Bízom benne, hogy sokan lesznek, akik megértik a logikai programozás "lelkét", és nagy lendülettel kezdenek hozzá a gyakorlati programozáshoz. Ezeknek az olvasóknak szeretnék a figyelmükbe ajánlani néhány olyan könyvet, amelyek részletesen foglalkoznak azokkal a kérdesekkel, amelyeket csak érinteni tudtunk e könyvben. • Ha valakit érdekel, hogy milyen más újelvû nyelv létezik, illetve létezett a nagyvilágban, javaslom a NIM IGÜSZI Mesterséges Intelligencia csoportjának 1974-es kiadványát, Tóth Péter: "Újelvû programozási nyelvek összehasonlitó vizsgálata" c. tanulmányát Ebben a kiadványban találhatjuk meg a PROLOG-szerû nyelvek szemléletének, gondolkodásmódjának egyik legkövetkezetesebb leírását, és egyben a

szerzõ elhelyezi a PROLOG-ot abban a perspektívában, amelybõl az is megsejthetõ talán, hogy mi jön majd a PROLOG után [1]. • Magyar nyelven Szeredi Péter-Futó Iván: "PROLOG KÉZIKÖNYV" c. könyve érhetõ el, amely 1977-ben jelent meg elõször a NIM IGÜSZI Software-fejlesztési Osztályának kiadásában. Azóta a Számítástechnikai Kutató és Innovációs Központ többször is kiadta, de csak kis példányszámú belsõ kiadványokban [4]. A Mûszaki Könyvkiadó gondozásában 1989-ben fog megjelenni "MPROLOG" címmel Farkas Zsuzsa, Futó Iván, Langer Tamás, Marosi István és Szeredi Péter új könyve [7]. Ez a könyv az MPROLOG sajátosságait is figyelembe véve részletesen tárgyalja a PROLOG programozás eszközeit és tulajdonságait. Magyar fordításban is elérhetõ Zohar Manna: "Programozáselmélet" c. könyve, amely többek között a PROLOG programozás elméleti hátterét, az elsõrendû matematikai logikát és a

rezolúciós tételbizonyitás módszerét is tartalmazza [8]. • Az angol nyelvû irodalom sokkal bõvebb. A legtöbb angol és amerikai egyetemen a számítógéptudomány hallgatóinak W.FClocksin és CSMellish: "Programming in Prolog" c könyvét ajánlják [9] Nemrég jelent meg Ivan Bratko "PROLOG Programming for Artifcial Intelligence" c. könyve az Addison-Wesley könyvkiadó gondozásában, amely fóleg a Mesterséges Intelligencia kurzusok hallgatóinak ad kitünõ tankönyvet [10]. Rendkívül hasznos olvasmány H.Coelho, JCCotta és LMPereira: "How to solve it with PiOLOG" c könyve, amely 1980-ban a legkülönbözõbb témájú PROLOG programokat gyûjtötte össze, és publikálta - minden kommentár nélkül [11]. A logikai feladatmegoldás elméletérõl és gyakorlatáról szól Robert Kowalski: "Logic for Problem Solving" c. könyve, amely a programozást megelõzõ fázishoz, az absztrakt probléma megoldáshoz ad kitünõ

anyagot, a PROLOG elméleti hatterét és esetleges utódait is tárgyalja [12]. • Ha valakit érdekel a logikai programozás matematikai háttere, az elsõ-, másod- és magasabb rendû matematikai logikai nyelvek alapvetõ tulajdonságai, kapcsolatai és külöbségéi, H.Andréka, TGergely, INémeti: "Easily comprehensible mathematical logic and its model theory" c. tanulmányát javaslom elolvasni, amely 1975-ben jelent meg a Központi Fizikai Kutató Intézet KFKI-75-24 számú kiadványában [13]. Másolatok elérhetõk az MTA Matematikai Kutató Intézetben Andréka Hajnalnál. • A gyakorlati PROLOG programozáshoz feltétlenül szükséges az "MPROLOG Language Reference" és a "Logic-Lab Reference" kézikönyv amelyek a Logicware és a Számítástechnikai Kutató és Innovációs Központ közös kiadványai [14,15]. Ugyanitt számos publikáció látott napvilágot MPROLOG-ban írt mûszaki alkalmazásokról és szakértõi rendszerekrõl

[17]. IRODALOMJEGYZÉK [1] Tóth P. (1974): Az új-elvû programozási nyelvek összehasonlító vizsgálata NIM IGÜSZI Software-fejlesztési Osztály Merterséges Intelligencia csoportja. [2] Andréka H., Németi I (1973): Programíró és programhelyesség hizonyító programok, Számológép kiskönyvtár [3] Battani, G., Meloni, H (1973): Interpreteur du language de programmation PROLOG, Université dAix Marseille. [4] Szeredi P., Futó I (1977): PROLOG Kézikönyv, NIM IGÜSZI Software-fejlesztési Osztály [5] Márkusz, Z. (1982): Design in Logic, Computer-Aided Design vol14, No 6 [6] Domán A. (1986): A Three-dimensional Graphics in an Artificial Intelligence Language: PROLOG, Proceedings of SOCOCO86, Graz, Austtia. [7] Farkas Zs., Futó I, Langer T, Marosi I, Szeredi P: MPROLOG Mûszaki Könyvkiadó, megjelenés alatt [8] Manna, Z. (1981): Programozáselmélet, Mûszaki Könyvkiadó [9] Clocksin, W.F, Mellish, CS (1981): Programming in Prolog, Springer-Verlag [10] Bratko, I.

(1986): PROLOG Programming for Artificial Intelligence, Addison-Wesley [11] Coelho, H., Cotta, JC, Pereira, LM (1980): How to Solve it with PROLOG, Portugál Építésügyi Minisztérium Általános Mérnöki Laboratóriuma, Lissabon. [12] Kowalski, R. (1979): Logic for Problem Solving, North Holland [13] Andréka, H., Gergely, T, Nemeti, I (1985): Easily Comprehensible Mathematical Logic and its Model Theory, Central Research Institute for Physics, Hungarian Academy Sciences, KFKI-75-24. [14] MPROLOG Language Reference (1985) Logicware, SZKI. [15] Logic-Lab Reference (1985) Logicware, SZKI. [16] MPROLOG Graphics on IBM PC-DOS (1985) SZKI. [17] Expert System Projects in MPROLOG (1986) SZKI. FÜGGELÉK I. MEGOLDÁSOK A GYAKORLATOKRA l. PROGRAM: FAGYLALT 1. Módosítsa úgy a programot,hogy a vasarol definíciója még egy feltételt tartalmazzon! Például: A Fagylalt-ot megvasaroljuk, ha a Fagylalt finom, kapható és olcsó. Megoldás: Egészítsük ki a vasarol definícióját egy

új feltétellel, és soroljuk fel az olcsó fagylaltokat. vasarol (Fagylalt) :finom(Fagylalt),kaphato(Fagylalt),olcso(Fagylat). olcso(malna). olcso(csoki). olcso(eper). 2. Mi történné akkor, ha a finom jelenlegi definícióját kicserélnénk a következõ, egy tényállításból álló definícióra? finom(Y). Mit jelent ez a tényállítás? Megoldás: A finom(Y). tényállítás azt jelenti, hogy bármelyik fagylalt finom, hiszen az Y változóval bármelyik konstans illeszthetõ. Ekkor a vasarol szabálynak csak egy igazi feltétele lenne, a kaphato(Fagylalt) Ez azt jelenti, hogy a ? fagylalt, fail. feladatot futtatva az összes kapható fagylaltot kiadná a rendszer. 3. Módosítsa úgy a programot, hogy az otthon maradáshoz feltétel járuljon! Például: Otthon maradunk akkor, ha esik az esõ. Megoldás: otthon marad:- esik. esik. 4. Módosítsa úgy a programot, hogy egyáltalán ne tudjunk fagylaltot enni! Megoldás: Módosítsuk úgy az adatbázist, hogy egyetlen

finom fagylalt se legyen kapható, és az otthon marad. tényállításból csináljunk egy olyan szabályt, amelynek a feltétele nem teljesül. (Otthon maradunk, ha esik az esõ de most nem esik az esõ.) Például: module fagylalt. body. fagylalt :vasarol(Fagylalt), nl, write("Menj a boltba es vegyel "), write(Fagylalt), write(" fagylaltot!"), nl. fagylalt :otthon marad, nl, write("Maradj otthon es egyel "), write("olyan fagylaltot, ami van!"), nl. vasarol(Fagylalt) :finom(Fagylalt), kaphato(Fagylalt). finom(rum). finom(csoki). finom(eper). finom(malna). kaphato(vanilia). kaphato(tutti frutti). otthon marad :- esik. esik:- fail. endmod /* fagylalt /. 2. PROGRAM: CSALÁD l. Definiálja a nagymama relációt! Megoldás: nagymama(NAGYI, UNOKA):- anya(NAGYI, SZULO), szulo(SZULO, UNOKA). 2. Bõvítse az adatbázist a testvér relációval: testver(kati, peter). testver(kati, erik). testver(peter, erik). /* Kati testvere Peternek / /* Kati

testvere Eriknek / /* Peter testvere Eriknek / Elég ennyi tényállítás? Mi lesz a válasz arra a kérdésre, hogy Erik testvére-e Péternek? Azaz milyen választ ad a PRÓLOG rendszer a következõ feladatra? ? testver(erik, peter). Megoldás: Nem elég ennyi tényállítás. A testver(erik, peter) feladatra No választ kapnánk Ki kell bõvíteni az adatbázist még három tényállítással: testver(kati,peter). testver(kati,erik). testver(peter,erik). testver(peter,kati). testver(erik,kati). testver(erik,peter). /* Kati testvere Peternek / /* Kati testvere Eriknek / /* Peter testvere Eriknek / /* Peter testvere Katinak / /* Erik testvere Katinak / /* Erik testvere Peternek / 3. Elég-e a testver definíciójához az elõzõ, 2 gyakorlatban szereplõ három tényállítás, ha a következõ fiver vagy nover definíciót csatoljuk a programhoz? fiver vagy nover(X, Y):- testver(X, Y). fiver vagy nover(X, Y):- testver(Y, X). Mi lesz a válasz a ? fiver vagy nover(erik, peter).

feladatra? Mit fejez ki a fiver vagy nover definíciója? Megoldás: Igen, hiszen a fiver vagy nover definíciója éppen azt fejezi ki, hogy a testver reláció szimmetrikus, tehát ha X testvére Y-nak, akkor Y is testvére X-nek. A fiver vagy nover(erik, peter). feladatra a válasz Yes lesz 4. Igaz-e az, hogy ? utod(robert, robert). Miért? Megoldás: Nem. Definíció szerint Róbert nem utódja önmagának, mivel az utod definíció mindkét szabályában apai ágon legalább egyet kell lépni a családfában. 3. PROGRAM: NAGY CSALÃD 1. Definiálja a nagynéni és a nagybácsi relációkat! Megoldás: nagyneni(A, GYEREK):nover(A, VALAKI), szulo(VALAKI, GYEREK). nagybacsi(O, GYEREK):fiu testver(O, VALAKI), szulo(VALAKI, GYEREK). 2. Mi lesz a válasz erre a feladatra: ? not(not(hazastars(anna, robert))). Megoldás: Yes (igen), mivel hazastars(anna, robert) not(hazastars(anna, robert)) not(not(hazastars(anna, robert))) igaz, nem igaz, igaz. 4. PROGRAM: TERV 1. Veszítenénk-e

információt, ha elhagynánk a terv(BEJARAT,AJTO1,ABLAK1,AJTO2,ABLAK2) öt argumentumú relációból a negyedik, AJTO2 argumentumot? A terv szabály a következõképpen módosulna: terv(BEJARAT,AJTO1,ABLAK1,ABLAK2) :elso szobi(BEJARAT,AJTO1,ABLAK1 ), szoba(AJTO2,ABLAK2), atjaras(AJTO1,AJTO2), not(szemben(ABLAK1,ABLAK2)). Megoldás: Nem veszítenénk információt, mivel az AJTO1 és AJTO2 ugyanarra az ajtóra vonatkozik, és elég tudni az AJTO1 értékét. AJTO2 értéke mindig AJTO1 ellentétes iránya 2. Módosítsa úgy az elso szoba szabályt, hogy az elsõ szobából még egy ajtó nyíljon Megoldás: elso szoba(BEJARAT,AJTO1,AJTO2,AJTO):szoba(AJTO1,AJTO), szoba(AJTO2,AJTO), not(equal(AJTO1,AJTO2)), not(equal(BEJARAT,AJTO1)), not(equal(BEJARAT,AJTO)), not(equal(BEJARAT,AJTO2)). 3. Bõvítse ki úgy a programot, hogy az elsõ szobából ne egy, hanem két szoba nyíljon Az új, harmadik szoba ablaka legyen ugyanolyan tájolású, mint a második szoba ablaka. Megoldás: module

terv. body. terv(BEJARAT,AJTO1,AJTO2,ABLAK1,ABLAK2,ABLAK3) :elso szoba(BEJARAT,AJTO1,AJTO2,ABLAK1), szoba(AJTOX,ABLAK2), atjaras(AJTO1,AJTOX), szoba(AJTOY,ABLAK3), atjaras(AJTO2,AJTOY), not(szemben(ABLAK1,ABLAK2)), equal(ABLAK2,ABLAK3). elso szoba(BEJARAT,AJTO1,AJTO2,ABLAK) :szoba(AJTO1,ABLAK), szoba(AJTO2,ABLAK), not(equal(BEJARAT,AJTO1)), not(equal(BEJARAT,ABLAK)), not(equal(BEJARAT,AJTO2)), not(equal(AJTO1,AJTO2)). szoba(AJTO,ABLAK) :irany(AJTO), irany(ABLAK), not(equal(ABLAK,eszak)), not(equal(AJTO,ABLAK)). atjaras(AJTO1,AJTO2):- szemben(AJTO1,AJTO2). irany(eszak). irany(del). irany(kelet). irany(nyugat). szemben(eszak,del). szemben(del,eszak). szemben(kelet,nyugat). szemben(nyugat,kelet). equal(X,X) endmod /* terv /. Programfutások: ? terv(nyugat,AJTO1,AJTO2,ABLAK1,ABLAK2,ABLAK3). AJTO1 = eszak AJTO2 = kelet ABLAK1 = del ABLAK2 = kelet ABLAK3 = kelet Continue (y/n) ? y AJTO1 = eszak AJTO2 = del ABLAK1 = kelet ABLAK2 = kelet ABLAK3 = kelet Continue (y/n) ? y AJTO1 = del AJTO2

= eszak ABLAK1 = kelet ABLAK2 = kelet ABLAK3 = kelet Continue (y/n) ? y AJTO1 = kelet AJTO2 = eszak ABLAK1 = del ABLAK2 = kelet ABLAK3 = kelet Continue (y/n) ? y NO 31. ábra Három szobából álló lakrész 5. PROGRAM: ÖSSSZEG 1. Módosítsa úgy az osszeg2 definícióját, hogy az elsõ számtól számított minden 5 számot adja össze növekvõ sorrendben. Ellenõrizze, hogy az utolsó és az elsõ szám különbsége osztható-e 5-tel! Megoldás: osszeg2(ELSO, UTOLSO, VEGOSSZEG) :0 is (OSSZEG - ELSO) mod 5, ciklus2(ELSO, UTOLSO, 0, VEGOSSZEG). ciklus2(B, UTOLSO , RESZOSSZEG, VEGOSSZEG) :B < = UTOLSO, S1 is B + RESZOSSZEG, B1 is B + 5, ciklus2 (B1, UTOLSO, S1, VEGOSSZEG). ciklus2(UTOLSO , UTOLSO, RESZOSSZEG, VEGOSSZEG) :VEGOSSZEG is UTOLSO + RESZOSSZEG. Programfutások: ? osszeg2(0, 20, VEGOSSZEG). VEGOSSZEG = 50 Continue (y/n) ? y NO ? osszeg2(3,43 VEGOSSZEG). VEGOSSZEG = 207 Continue (y/n) ? y NO ? osszeg2(2,14,VEGOSSZEG). NO 2. Módosítsa úgy az osszeg2

definícióját, hogy az a számokat csökkenõ sorrendben adja össze! Megoldás: Cseréljük ki a osszeg2 szabályban a ciklus2 részfeladat elsõ két argumentumánák a sorrendjét. A ciklus2 definíción ne változtassunk: osszeg2(ELSO, UTOLSO, VEGOSSZEG) :ciklus2(UTOLSO, ELSO, 0, VEGOSSZEG). ciklus2(B, UTOLSO, RESZOSSZEG, VEGOSSZEG) :B < = UTOLSO, S1 is B + RESZOSSZEG, B1 is B + 1, ciklus2(B1, UTOLSO, S1, VEGOSSZEG). ciklus2(UTOLSO, UTOLSO, RESZOSSZEG, VEGOSSZEG) :VEGOSSZEG is UTOLSO + RESZOSSZEG. Programfutások: ? osszeg2(10,1,VEGOSSZEG). VEGOSSZEG = 55 Continue (y/n) ? y NO ? osszeg2(3,13,VEGOSSZEG). NO 3. Bõvítse úgy az osszeg2 definícióját, hogy az a számokat növekvõ és csökkenõ sorrendben is össze tudja adni! Megoldás: Álljon az osszeg2 definíciója két szabályból. Az elsõ kezelje azt az esetet, amikor növekvõ sorrendben adjuk össze a számokat, a második pedig azt az esetet, amikor csökkenõ sorrendbe. osszeg2(ELSO, UTOLSO, VEGOSSZEG)

:ciklus2(ELSO, UTOLSO, 0, VEGOSSZEG). osszeg2(ELSO, UTOLSO, VEGOSSZEG) :ciklus2(UTOLSO, ELSO, 0, VEGOSSZEG). ciklus2(B, UTOLSO, RESZOSSZEG, VEGOSSZEG) :B < = UTOLSO, S1 is B + RESZOSSZEG, B1 is B + 1, ciklus2(B1, UTOLSO, S1, VEGOSSZEG). ciklus2(UTOLSO, UTOLSO, RESZOSSZEG, VEGOSSZEG) :- VEGOSSZEG is UTOLSO + RESZOSSZEG. Programfutások: ? osszeg2(2,20, VEGOSSZEG). VEGOSSZEG = 209 Continue (y/n) ? y NO ? osszeg2(8,1, VEGOSSZEG). VEGOSSZEG = 36 Conitnue (y/n) ? y NO 6. PROGRAM: TÉGLALAP 1. Definiálja három téglalap metszetét! ? harom metszet(A,B,C,R). Megoldás: Kibõvítettük adatbázist egy 7. számú téglalappal, hogy látványos példánk legyen a három téglalap metszetére: teglalap(7,r(p(5,4),p(16,14))). Három téglalap metszetének szabálya: harom metszete(A,B,C,R):teglalap(A,R1), teglalap(B,R2), teglalap(C,R3), metsz(R1,R2,RX), metsz(RX,R3,R). Programfutások: ? harom metszete(7,3,C,R). C=2 R = r(p(7,10),p(10,12)) Continue (y/n) ? y C=3 R = r(p(7,8),p(15,12))

Continue (y/n) ? y C=4 R = r(p(13,8),p(15,12)) Continue (y/n) ? y C=6 R = r(p(15,8),p(15,8)) Continue (y/n) ? y C=7 R = r(p(7,8),p(15,12)) Continue (y/n) ? y NO 32. ábra Három téglalap metszete 2. Írjon egy olyan szabályt, amely egy téglalap négy csúcspontját határozza meg az adatbázisban rögzített adatok alapján! 33. ábra A téglalap csúcspontjai ? csucsok (N,A,B,C,D) Megoldás: csucsok(N, p(X1,Y2), p(X1,Y1), p(X2,Y1), p(X2,Y2)):teglalap(N, r(p(X1,Y1), p(X2,Y2))). Programfutás: ? csucsok(3, A,B,C,D). A = p(7,12) B = p(7,8) C = p(15,8) D = p(15,12) Continue (y/n) ? y NO 3. Határozza meg két téglalap közös csúcspontját, ha van! Segítség: módosítsa a szamol definíciót! Megoldás: Egy téglalap akkor zsugorodik pontra, ha a két átlós irányú csúcspontja egybe esik. Ennek megfelelõen a szamol definíciót módosítottuk, a harmadik feltétel min(X,Y,X) helyett equal(X,Y) lett. Íme a program: module csucs. body. kozos csucs(A,B):teglalap(A,R1),

teglalap(B,R2), metsz1(R1,R2,V), nl, write(V). kozos csucs(A,B) :nl, write("Nincs kozos csucs.") metsz1(r(p(X1,Y1),p(X2,Y2)), r(p(K1,L1),p(K2,L2)), p(M,N)):szamol1 (X1,X2,K1,K2,M,M), szamol1(Y1,Y2,L1,L2,N,N). szamol1 (A,B,C,D,X,Y) :max(A,C,X), min(B,D,Y), equal(X,Y). min(X,Y,X) :- X < Y. min(X,Y,Y). max(X,Y,X) :- X > Y. max(X,Y,Y). equal(X,X). teglalap(1,r(p(4,2),p(8,6))). teglalap(2,r(p(6,10),p(10,15))). teglalap(3,r(p(7,8),p(15,12))). teglalap(4,r(p(13,4),p(18,14))). teglalap(5,r(p(5,3),p(6,5))). teglalap(6,r(p(15,3),p(18,8))). teglalap(7,r(p(5,4),p(16,14))). endmod /* csucs /. Programfutások: ? kozos csucs(3,6). p(15,8) Yes ? kozos csucs(1,3). Nincs kozos csucs. Yes 7. PROGRAM: RAJZ 1. Milyen hibát okozna, ha a teglalap vonala szabályból elhagynánk a reset heading beépített eljárást? Segítség: Gondoljunk arra, hogy általában egynél több téglalapot szeretnénk rajzolni. Megoldás: A reset heading a téglalap kirajzolása után visszaállítja a

sas irányát alaphelyzetbe: 34. ábra A sas iránya alaphelyzetben Ha ezt kihagyjuk, a következõ téglalap rajzolását rossz irányba kezdi el rajzolni: 35. ábra A sas iránya nincs alaphelyzetben 2. Írjon egy olyan szabályt, amely az adatbázis összes téglalapját kirajzolja! Használja a fail által kiváltott visszalépéses módszert! Megoldás: mindet:kezdet(1,3), rajz(N), fail. mindet:nl, write("Nincs tobb teglalap."), nl Ha kiadjuk a mindet célállítást, a program kirajzolja az elsõ téglalapot majd ha a folytassuk? kérdésre i-vel (igennel) válaszolunk, kirajzolja a másodikat. És így tovább Az utolsó téglalap kirajzolása után kiírja, hogy Nincs tobb teglalap., és végül, hogy Yes és leáll A képernyõ grafikus üzemmódban marad, ezért célszerû kiadni a vege célállítást: vege:clear graphic screen, screenmode < < < text. amely átváltja a képernyõt szöveges üzemmódba. 3. Kétszeres nagyítátst vagy kicsinyítést

érünk-e el azzal, ha a scale állapotjelzõ alapállapotát: scale < < < [1024,320] megváltoztatjuk scale < < < [1024,160]-ra? Mi lesz a hatása a scale < < < [2048,320] használatának? Megoldás: Kétszeres kicsinyítést érünk el mindkét esetben, hiszen, egy logikai egységre kétszer kevesebb fizikai egység jut mindkét esetben. 4. Nem kényelmes dolog színek helyett számokat megnevezni Bõvítse a programot olyan adatbázis definíciókkal, amelyek a színek kódjához a színek nevét rendeli. Módosítsa úgy a programot, hogy az abra célállításban a téglalap sorszámát követve megadhassuk a képernyõ és a toll színét szavakkal! Megoldás: module rajz1. body. abra(N, SZIN, TOLLSZIN):screenmode < < < graphic, reset, reset heading, szinek(SZIN,TOLLSZIN,N1,N2), color < < < N1, pencolor < < < N2, rajz(N). szinek(SZIN, TOLLSZIN, N1, N2):hatter szin(N1, SZIN), toll szin(N2,TOLLSZIN). hatter szin(0, fekete).

hatter szin(1, kek). hatter szin(2, zold). hatter szin(3, turkiz). hatter szin(4, piros). hatter szin(5, lila). toll szin(0, hatter):- palette < < < 1. toll szin(1, turkiz):- palette < < < 1. toll szin(2, lila):- palette < < < 1. toll szin(3, feher):- palette < < < 1. toll szin(1, zold):- palette < < < 0. toll szin(2, piros):- palette < < < 0. toll szin(8, sarga):- palette < < < 0. rajz(N):teglalap(N, r(p(X1,Y1),p(X2,Y2))), Z1is X1*40, W1 is (Y140) + 100, Z2 is X2,40, W2is (Y2*40) + 100, X is Z2-Z1, Y is W2-W1, teglalap vonala(Z1,W1,X,Y), folytat. teglalap(1,r(p(4,2)p8,6))). teglalap(2,r(p(6,10),p(10,15))). teglalap(3,r(p(7,8),p(15,12))). teglalap(4,r(p(13,4)p(18,14))). teglalap(5,r(p(5,3)p(6,5))). teglalap(6,r(p(15,3)p(18,8))). teglalap vonala(Z1W1,X,Y):position < < < [Z1,W1,0], pen < < < down, forward(X), turn(90), forward(Y), turn(90), forward(X), turn(90), forward(Y), reset heading. folytat:position

< < < [200,20,0], text("folytassuk? (i/n)"), read token(X), valasz(X). valasz(n):clear graphic screen, screenmode < < < text, cut input, !. valasz(i):- cut input, !. valasz(X):- cut input, folytat. vege:clear graphic screen, screenmode < < < text. endmod /* rajz1 /. 8. PROGRAM: SZINEZ 1. Bõvítse a programmenüt a következõ kérdéssel: Melyik téglalapot kívánja kirajzolni? (1-6) Ellenõrizze, hogy a válasz helyes-e? Mely definíciók szükségesek a RAJZ programból ahhoz, hogy a kívánt téglalapot lehessen kirajzolni? Megoldás: Az alábbiakban közöljük a módasított programot. A melyik definícióban van az új kérdés A RAJZ programból a rajz a teglalap definíciókat hoztuk át. A rajz definícióból kihúztuk a folytat részfeladatot module szinez. body. szinez:screenmode < < < text, nl, write("Jo napot! Segiteni szeretnek szineket valogatni."), nl, hatter(SZIN), toll(TOLLSZIN), melyik(N),

kezdet(SZIN,TOLLSZIN), rajz(N), vege. hatter(SZIN):- menu1, ellenor(SZIN,0,5). toll(TOLLSZIN):- menu2, ellenor(TOLLSZIN,0, 3). melyik(N):- kerdes, ellenor(N,1,6). menul:nl, write("A hatter szinvalaszteka:"), nl, write(" 0. fekete "), nl, write(" 1. kek "), nl, write(" 2. zold "), nl, write(" 3. turkiz "), nl, write(" 4. piros "), nl, write(" 5. lila "), nl, write("Kerem a valasztott szin sorszamat."), nl menu2:nl, write("A toll szinvalaszteka:"), nl, write(" 0. hatter "), nl, write(" 1. turkiz "), nl, write(" 2. lila "), nl, write(" 3.feher "), nl, write("Kerem a valasztott szin sorszamat."), nl kerdes:nl, write("Melyik teglalapot kivánja kirajzolni? (1-6)"), nl. ellenor(N, TOL, IG):read token(N), N is a integer, N >= TOL, N < = IG,cut input,!. ellenor(N,X,Y):- hiba,ellenor(N,X,Y). hiba:- read token (X),cut input,nl,

write("Hibas szam. Probalja ujra!"),nl kezdet(SZlN,TOLLSZIN):screen < < < graphic, reset, reset heading, color < < < SZIN, pencolor < < < TOLLSZIN. teglalap vonala (Z1,W1,X,Y):position < < < [Z1,W1,0], pen < < < down, forward(X), turn(90), forward(Y), turn(90), forward(X), forward(90), forward(Y), reset heading. vege:- position < < < [100,20,0], text("Vissza karakteres modba? (i/n)"), read token(X), vegez(X). vegez(i):- clear graphic screen, screenmode < < < text, cut input, !. vegez(n):- clear graphic screen, cut input, !. vegez(X):- cut input, vege. rajz(N):teglalap(N, r(p(X1,Y1 ),p(X2,Y2))), Z1 is X1*40, W1 is (Y140) + 100, Z2 is X240, W2 is (Y2*40) + 100, X is Z2-Z1, Y is W2-W1, teglalap vonala(Z1,W1,X,Y). teglalap(1,r(p(4,2),p(8,6))). teglalap(2,r(p(6,10),p(10,15))). teglalap(3,r(p(7,8),p(15,12))). teglalap(4,r(p(13,4),p(18,14))). teglalap(5,r(p(5,3),p(6,5))). teglalap(6,r(p(15,3),p(18,8))).

endmod /* szinez /. 2. Módosítsa úgy a menu1 kérdésére adandó válasz formáját, hogy ne a szín sorszámát, hanem magát a szín nevét kelljen beütni. Hogyan oldaná meg a válasz ellenõrzését? Segítség: készítsen egy adatbázis-definíciót a színekrõl! Megoldás: A módosításnál arra kell ügyelni, hogy a szín sorszáma továbbra is értékes információ, amit tudnunk kell. Ezért a jo szin definíció két argumentumú. Íme a programrészlet: hatter(SZIN):- menu1, ellenor(NEV,SZIN) menu1:nl, write("A hatter szinvalaszteka:"), nl, write(" 0. fekete "), nl, write(" 1. kek "), nl, write(" 2. zold "), nI, write(" 3. turkiz "), nl, write(" 4. piros "), nl, write(" 5. lila "), nl, write("Kerem a valasztott szin nevet."), nl ellenor(NEV,SZIN):read token(NEV), jo szin(NEV,SZIN), cut input, ! ellenor(NEV,SZIN):- hiba1, ellenor(NEV,SZIN). jo szin(fekete,0) jo szin(kek,1). jo szin(zold,

2). jo szin(turkiz,3). jo szin(piros, 4). jo szin(lila, 5). hibal:- read token(X), cut input, nl, write(" Hibas szin. Probalja ujra!"), nl 3. Milyen baj történne, ha a hiba definícióból elhagynánk a hibás válasz lenyelését, azaz a read token(X) részfeladatot? Hogyan mûködne a program? Megoldás: Nézzük meg a módosított programrészletet az ellenor definícióval együtt: ellenor(N, TOL, IG):read token(N), N is a integer, N > = TOL, N < = IG, cut input, !. ellenor(N,X,Y):- hiba, ellenor(N, X, Y). hiba:- cut input, nl, write("Hibas szam. Probalja ujra!"), nl A módosítás hatása nagyon kellemetlen lesz: A prorgram végtelen ciklusba esik. A hibaüzenetet írja ki a program rendületlenül, olyan sokszor, ameddig hagyjuk, ameddig le nem lõjük a programot. Ennek a jelenségnek az az oka, hogy a hibás bemenõ adaton a program visszalép, és ugyanazt az adatot beolvassa újra. Persze másodszor is pont olyan hibás, mint elõször, és

ezért az eljárás megismétlõdik. A hibás adat okozta végtelen ciklust úgy is el lehet kerülni, hogy az ellenor definícióban közvetlenül a read token után hívjuk meg a cut input beépíteitt eljárást, mielõtt még leellenõriztük volna a beolvasott adatot. Így idõben tiltjuk le a visszalépést a read token által beolvasott hibás adaton: ellenor(N, TOL, IG):read token(N), cut input, N is a integer, N > = TOL, N < = IG, !. ellenor(N, X, Y):- hiba, ellenor(N, X, Y). hiba:- cut input, nl, write(" Hibas szam. Probalja ujra!"), nl 9. PROGRAM: LISTA 1. Mi lenne a hatása annak, ha az eleme definíció elsõ szabályába egy cut szimbóumot tennénk? eleme(X, [X¦T]):- !. eleme(X, [Y¦T]):- eleme(X,T). Hány megoldása lenne az ? eleme(X, [a,b,c]). feladatnak? Megoldás: A cut letiltja a visszalépést a definícióban, így csak egy elemet fog az eleme találni: ? eleme(X,[a,b,c]). X=a Continue (y/n) ? y NO 2. Szimmetrikus-e a kihagy eljárás, azaz

lehet-e vele elemet beszúrni egylistába? Mi lesz az eredménye a következõ feladatnak: ? kihagy(b,L,[x,y,z]). Megoldás: Igen,lehet vele elemet beszúrni egy listába: ? kihagy(b,L,[x,y,z]). L = [b,x,y,z] Continue (y/n) ? y L = [x,b,y,z] Continue (y/n) ? y L = [x,y,b,z] Melyik az a lista,amelybõl a b elemet kihagyva [x,y,z] listát kapunk? Continue (y/n) ? y L = [x,y,z,b] Continue (y/n) ? y NO 3. Definiálja a diszjunkt listák szabályát! Segítség: két lista akkor diszjunkt, ha nincs közös elemük Megoldás: Írunk egy rekurzív definíciót. Az elsõ állítás azt rögzíti, hogy az üres listának egyetlen listával sincs közös eleme A második szabály a rekurzív újrahívás. Két lista diszjunkt, ha az elsõ lista feje nem eleme a második listaának, és a törzse diszjunkt a második listáva: diszjunkt([ ], L2). diszjunkt([H¦T], L2):not eleme(H, L2), diszjunkt(T, L2). Programfutások: ? diszjunkt ([a,b,c,d],[x,y]). Yes ? diszjunkt([a,b,c,d],[x,b,z]). NO ?

diszjunkt([ ],[ ]). Yes ? diszjunkt([a,b],[ ]). Yes 4. Olvassa el figyelmesen az alábbi programot, amely számlisták sorbarendezését végzi el a beszúrásos módszerrell Írja le szavakkal a definíciókat! rendez([H¦T},S):- rendez(T,L),beszur(H,L,S). rendez([ ],[ ]). beszur(X,[H¦T],[H¦L]):- H < X,!,beszur(X,T,L). beszur(X,L,[XL]). Megoldás: Egy számlista növekvõ érték szerinti sorbarendezése rekurzív módon történik. A [H¦T] listából rendezett listát kapunk (S), ha a T törzset rendezzük, és a H fejet nagyság szerinti helyére beszúrjuk a törzs rendezett listájába. Az üres lista rendezett formája maga az üres lista. Az X elem nagyság szerinti beszúrása egy [H¦T] listába úgy történik, hogy ha az X elem nagyobb a lista fejénél, a Hnál, akkor beszúrjuk a lista törzsébe, T-be. Ha nem nagyobb (kisebb vagy egyenlõ), akkor az X elem lesz az új lista elsõ eleme. A beszur definícióban található cut eredményeképpen a két állítás

kizárásos alapon mûködik. Az elsõ akkor és csak akkor lesz igaz, ha az X kisebb mint H, a második akkor és csak akkor, ha nem. 5. Miért nem jó az alábbi definíció: kapcsol([ ], L2, L2). kapcsol(L1, [ ], L1). kapcsol(L1, L2, L1¦L2). Megoldás: A definíció harmadik szabálya teljesen rossz, mivel két listát nem lehet úgy összekapcsolni, hogy az elsõ listát az új·lista fejének, a másodikat az új lista törzsének fogjuk fel. Egyébként még szintaktikusan is helytelen mivel L1¦L2 t szögletes zárójelek közé kell tenni [L1¦L2], különben hibaüzenetet kapunk Például, ha L1 = [a,b] és L2 = [x,y], akkor [Ll¦L2] = [[a,b],x,y] és semmi esetre sem [a,b,x,y]. 10. PROGRAM: ÉPÜLET 1. Illeszthetõ-e az alábbi két összetett kifejezés, ha az operátorok deklarációi a következõk: operator(., rl, 50) operator( #,rl, 60). A. BCnil és X#YT Megoldás: Igen, hiszen a zárójelezett alakjuk A.(B(C nil)) és (X#Y) T így a második kifejezés

felfogható egy olyan listának, ahol a lista feje összetett kifejezés. A listák feje A és X # Y illeszthetõ. A listák törzse B.Cnil és T illeszthetõ 2. Illeszthetõk-e az 1 gyakorlat kifejezései, ha a # operátor prioritása kisebb, mint a pont operátoré? A feladat így módosul: operator(.,rl,50) operator( #, rl,40). A. BCnil és X # YT Megoldás: Nem, hiszen zárójelezett alakjuk A.(B(C nil)) és X#(Y T) Így a második kifejezés felfogható egy olyan kételemû listának, ahol a lista operátora #, és ez nem illeszthetõ a pont operátorral. 3. Legyen az operátorok deklarációja: operátor(., rl, 50) operator(+ +,lr, 40). Adja meg a következõ két kifejezés zárójelezett formáit és rajzolja fel a megfelelõ bináris fákat: A . B C ++ D és X ++ Y ++ Z W Megoldás: (A.(BC)) + + D (X + + Y) + + (Z.W) 11. PROGRAM: KOCKA 1. Az egyet fordit definíció a z és az y tengely körüli 90 fokos elforgatást definiálja Hogyan lehetne az x tengely körüli

elforgatást leírni? 36. ábra A kocka elforgatása az X tengely körül Megoldás: Két irányban lehet a kockát az x tengely körül elforgatni, az óramutatóval megegyezõ és ellenkezõ irányban. A megfelelõ egyet fordit szabályok a következõk: egyet fordit( s(LENT,FENT,ELOL,HATUL,BAL,JOBB), s(ELOL,HATUL,FENT,LENT,BAL,JOBB) ). egyet fordit( s(LENT,FENT,ELOL, HATUL, BAL,JOBB), s(HATUL,ELOL,LENT,FENT,BAL,JOBB) ) 2. Hogyan lehetne a szinek dinamikus definíció kinyomtatását megoldani rekurzió nélkül? Segítség: gondoljon a visszalépés adta lehetõségekre! Megoldás: A nyomtat újrahívása helyett visszaléptetjük a programot faillel. A del nth statement eljárás a visszalépésre nem érzékeny, tehát a törölt eljárások nem élednek fel. Így minden egyes visszalépésnél a program nyomtat és töröl egy új szinek tényállítást: nyomtat :szinek (X,N), nl, write(X); write(" "), write(N), nl, del nth statement(szinek/2,1 ), fail. nyomtat. 3.

Tudjuk, hogy hat színnek sokkal több permutációja van (6! =1 * 2 3 4 5 6 = 720), mint ahány elforgatott helyzete a hatszínû kockának (24). Írjon egy olyan eljárást, amely azt ellenõrzi, hogy a hat ·szín egy adott permutációja megfelel-e a kocka egyik elforgatott helyzetének! Megoldás: proba(P):kocka(X), elforgat(X), fail proba(P):szinsor(P), nl, write("Igen, ez a kocka egyik helyzete."), nl, del all statements{szinsor/1). proba(P):nl, write("Nem, ilyen helyzet nincs."), nl, del all statements(szinsor/1). A proba(P) predikátum egyetlen argumentuma a bemenõ adatot, a színpermutációt tartalmazza. A proba definíció elsõ szabálya a kocka összes elforgatott helyzetét elõállítja, és tárolja a szinsor dinamikus definícióban. A második szabály akkor ad igazat, ha a szinsor(P) részfeladatot sikerül megoldani. A harmadik szabályra akkor kerül sor, ha a másodikból hamissal szállunk ki. Programfutások: ?

proba(s(kek,piros,fekete,feher,narancs,sarga)). Igen, ez a kocka egyik helyzete. Yes. ? proba(s(kek,piros,feher,sarga,fekete,narancs)). Nem, ilyen helyzet nincs. Yes. 12. PROGRAM: MODULOK 1. Tegyük fel, hogy két programozó írt egy-egy modult, amelyekben mindketten használták a nulla argumentumu alma predikátumnevet, és az egy argumentumu barack predikátumnevet. Késõbb kiderült, hogy a két modult össze kell kapcsolni, mégpedig úgy, hogy az alma eljárás az elsõ modulban legyen meghívva, és a második modulban definiálva. A barack predikátumnév viszont két teljesen különbözõ eljárást jelölt a két modulban, amelyek között semmi kapcsolat nem volt. Hogyan kell megadni a modul kapcsolatokat? Megoldás: Az elsõ modulban import(alma/0), a másodikban export(alma/0) speciflkáció kell, hogy szerepeljen a megfelelõ modulkapcsolatokban. Ha a barack predikátumnévnek két különbözõ jelentése van a két modulban, elvben nem kéne semmit változtatni a

programon, hiszen a predikátumnevek alapértelmezésben lokálisak a modulra nézve. A PDSS azonban figyelmeztetni fog bennünket, hogy a barack predikátumnév már szerepelt. A gyakorlat is azt mutatja, hogy érdemes az egyik modulban ezt a predikátumnevet megváltoztatni (mondjuk barack1-re). Ez azért is célszerû, mert így két különbözõ dolognak két különbözõ neve van, és így például a hibaüzeneteknél pontosan fogjuk tudni, hogy az melyik predikátumra vonatkozik. 2. Hiba-e, ha egy olyan predikátumnevet exportálunk, amely nincs definiálva a modulban? Megoldás: Igen. Hibaüzenetet fogunk kapni 3. Hiba-e, ha egy predikátumnevet exportálunk is, meg importálunk is ugyanabban a modulban? Megoldás: Igen. Hiszen ha a predikátum ebben a modulban van definiálva, és exportáljuk teljesen ésszerûtlen importálni, ha pedig a predikátum nincs ebben a modulban definiálva, nem lehet exportálni. II. IBM PC MPROLOG BEÉPÍTETT ELJÁRÁSAI • A következõkben

felsorolunk olyan MPROLOG beépített eljárásokat is, amelyekrõl a könyvben nem volt szó, hogy az Olvasó nagyobb betekintést kapjon az MPROLOG rendszer adta lehetõségekbe. Részletes információt az MPROLOG kézikönyv [14,15] tartalmaz. Az argumentumok jelölése: M D S C X,Y N R A Name Str L T Op Kind Prio Error ID VarList Rel modulnév a definíció leírója állítás feltétel kifejezések egész szám valós vagy egész szám az õsdefiníció neve név füzér lista kifejezéstípus neve az operátor neve az operátor fajtája az operátor prioritása hibaazonosító kifejezés változópárok listája reláció (< , > , = , < =, > =, = ) Char CC Token CH SI Flag egyetlen karakter a karakter kódja token csatorna állapotjelzésre használt kifejezés kapcsoló Ezeket a rövidítéseket számok követhetik (pl. C1, C2) Beépített eljárások: ! !(A) / + – C1 –> C2 C1 , C2 C1 ; C2 X=Y X=/=Y R1 = : = R2 R1 = / = R2 R1 < R2 R1 < = R2 R1

> R2 R1 > = R2 X==Y X==Y X@<Y X@<=Y X@>Y X@>=Y X^C C(L1, X, L2) accept char accept key add op(Op, Kind, Prio) add statement(S) add statement(S, X) add statement b(S) add statement b(S, X) ancestor(A) ancestor list(L) C1 and C2 any key bag of(X, C, L) channel state(CH, X) close input(CH) close output(CH) compare(Rel, X, Y) concatenate(Str1,Str2,Str3) concatenate list(L, Str) convert(X, Flag, Y) convert char(Char, CC) create dynamic(D) create external(D) create value(Name) levágja a visszalépést a definícióban levágja a visszalépést az A õs definícióig (õsdefiníció minden olyan definíció, amely a kiértékelendõ predikátumtól visszafelé a célállításig húzható kiértekelési láncon van) levágja a visszalépést az állításban (kisebb a hatásköre, mint a !-nek) levágja a visszalépést és sikerül levágja a visszalépest és meghiúsul logikai implikáció logikai ÉS logikai VAGY X illeszthetõ Y-nal X nem illeszthetõ Y-nal

aritmetikai egyenlõ aritmetikai nem egyenlõ aritmetikai kisebb aritmetikai kisebb vagy egyenlõ aritmetikai nagyobb aritmetikai nagyobb vagy egyenlõ a kifejezések azonosak a kifejezések nem azonosak X kifejezés kisebb, mint Y kifejezés X kifejezés kisebb vagy egyenlõ Y kifejezéssel X kifejezés nagyobb, mint Y kifejezés X kifejezés nagyobb vagy egyenlõ Y kifejezéssel egzisztenciális kvantor X az L1 lista feje, L2 a törzse lenyeli az elsõ karaktert a bemeneti folyamról egy billentyû leütését fogadja felvesz egy operátort és deklarálja felvesz egy állítást a definíció végére felvesz egy állítást meghatározott pozícióra visszaléptethetõ változat visszaléptethetõ változat az A definíció õse az aktuális feladatnak L az õsdefiníciók listája logikai ÉS (ua. mint a ,) ellenõrzi, hogy van-e olvasásra váró adat L az X-ek olyan listája, amelyekre C igaz X egy csatornaállapot lezár egy bemeneti csatornát lezár egy kimeneti csatornát

sikerül, ha X-re és Y-ra Rel igaz Str1 és Str2 összekapcsolása Str3 füzérré L lista elemeit összekapcsolja Str füzérré X-et Y-ba viszi át karaktert kódjába visz át dinamikus definíciót deklarál külsõ definíciót deklarál deklarál egy nevet, amely érték lesz current char code(CC) current input(CH) current key(CC) current output(CH) cursor(N1, N2) cut input definition in module(D,M) definition length(D,N) del all statements(D) del alI statements b(D) del definition(D) del matching statement(S) del matching statement b(S) del nth statement(S, N) del nth statement b(S, N) del op(Op, Kind) del statement(S) del statement b(S) designator of(S, D) display char code(CC) display string(Str) evaluable(X) evaluate(C) fail halt host(Command) host edit X is R X is a T D is defined Type key sort(L1, L2) list length(L,N) make char list(Str,L) make vars coded(X) matching anchestor(A) matching statement(S,N1,N2) matching statements(S) modify(CH) module state(M,X) new page

nl nl(N) not C nth statement(D, S, N) C1 or C2 op priority(Op,Kind,Prio) opt evnluate(X) opt nl output columns left(N) output length(X, N, Flag) output length(X, N) output line length(N) phrase(Type, L) prefix of(Str1, Str2) print(X) read(X) read(X,Prio) read comment(Str) read record(Str) read token(Token) a következõ bemenõ karakter kódja CC CH az aktuális bemeneti csatorna neve a következõ bemenõ karakter billentyûkódja CC CH az aktuális kimeneti csatorna neve A cursor az N1-edik sorban az N2-edik oszlopnál a beolvasott bemenõ folyamban letiltja a visszalépést D definíció az M modulban van D definíció N állítást tartalmaz törli a D definíció összes állítását visszaléptethetõ törlés törli a D dinamikus definíciót töröl egy illeszthetõ állítást visszaléptethetõ törlés az S definíció N-edik állítását törli visszaléptethetõ törlés törli az Op operátort, amely Kind fajtájú törli az elsõ állítást, amely illeszthetõ S-sel

visszaléptethetõ törlés az S állítás leírója a terminálon megjelenik a CC karakterkód a terminálon megjelenik az Str füzér X kiértékelhetõ kiértékeli C-t kudarc leállítja a program futását kiadja az operációs rendszer parancsát meghívja az operácicós rendszer szerkesztõprogramját R aritmetikai kiértékelése T a kifejezés típusa D típusa Type sorba rendezi az L1 listát, az eredmény az L2 lista N az L lista hossza Str füzér karaktereinek listája X-beli összes változót illeszti egy kóddal nem determinisztikus õs megkeresi S-t, N1-tõl és a száma N2-ben lesz keres egy állítást, amely illeszthetõ S-sel kiírja a tárcsatornát módosított formában ellenõrzi, hogy az M modul a program része, avagy sem; X értéke on vagy off új oldalt kezd sort emel N sort emel sikerül, ha evaluate(C) nem sikerül D definícióban az N-edik állítás S logikai VAGY (ua. mint a ;) Op prioritása Prio kiértékeli X-et, ha lehet, ha nem, nem sikerül

új sort kezd, ha szükséges ellenõrzi, hogy a sorban van-e még N karakter N karakternyi hely kell X kiírásához a Flag kapcsolóval N karakternyi hely kell X kiírásához N a maximális hossza a kiírás egy sorának az adott Type típus egy frázisá L Str2 prefixe Str1 megjeleníti X-et beolvas egy kifejezést beolvas egy kifejezést Prio prioritás figyelembevételével beolvas egy magyarázatot (/* . */) beolvas egy sort beolvas egy MPROLOG tokent remove spaces(Str1,Str2) repeat run goal(M) set channel(CH,X) set channel(CH,X) set cursor(N1,N2) set input(CH) set module state(M,X) set of(X,C,L) set output(CH) set state(SI,X) set value(Name,X) set value b(Name,X) signal(Error ID) signal(Error ID,C,N) skip layout skip unread input sort(L1,L2) state(SI,X) state indicator(SI) statement number(S,N1,N2) sorszámmal kezdõdõ definícióban string index(Str1,Str2,N) string length(Str,N) structure(X,FIag,Y) substring(Str1,N,Str2) substring(Str1,N1,N2,Str2) subtype(T1,T2) succeed

system(Action) terminal type(X) tokens unread(N) unread token(T,Flag) value(Name, X) var list(X , Y, Varlist) write(X) write(X, Flag) write char code(CC) write error(Error ID,C,N) write spaces(N) write tab(N) kihagyja a szóközöket Str1-bõl,az eredmény Str2 ismételten teljesül visszalépéskor az M modul célállítását futtatja beállítja egy csatorna tulajdonságait beállítja egy csatorna állapotát a cursort az adott sorra és oszlopra állítja bemenõ adatok a CH csatornáról a programhoz kapcsolja vagy leválasztja M modult X-ek rendezett listája L, amelyekre C igaz kimenõ adatok a CH csatornáról beállítja az SI állapotjelzõt X értékre beállítja a Name értékét X-re visszaléptethetõen beállítja Name értékét X-re hibát jelez a meghívó feltételben hibát jelez a C feltétel N-edik argumentumában átugorja a bemenõ folyamban lévõ szóközöket átugorja a még be nem olvasott szimbólumokat sorba rendezi L1listát SI értéke X SI egy

állapotjelzõ keres egy állítást,amely illeszthetõ S-sel, sorszáma N2 az N1-es Str2 pozíciója N a Str1-ben Str hossza N strukturák konverziója N-tõl kezdve Str1 részfüzére Str2 részfüzér N1-tõl N2 hosszúsággal T2 résztípusa T1 sikerül az adott MPROLOG rendszer Action cselekedetet hajt végre X egy terminál azonosító N token vár beolvasásra visszalép egy beolvasott tokenen a Name értéke X a szimbolikus változóneveket változókra cseréli kiír egy kifejezést kiír egy kifejezést a Flag szerint kiír egy karakter kódot kiírja az argumentumokat a terminálra kiír N szóközt az N-edik oszlopra tabulál III. AZ MPROLOG HASZNÁLATA IBM PC KÖRNYEZETBEN • Ez a függelék arra való, hogy az érdeklõdõ Olvasó ki tudja próbálni a példaprogramokat és a gyakorlatok megoldásait IBM PC kompabitilis személyi számítógépen. Az MPROLOG futtatásához legalább 512 Kbyte központi tár, egy merevlemez- és egy hajlékonylemez-egység, vagy két

hajlékonylemez-egység szükséges. Ebben a függelékben kizárólag a PDSS (Program Development Support System) ismertetésére szorítkozunk, annak is csak azt a kis részletét írjuk le, amelynek ismerete elengedhetetlen szükséges a program futtatásához. A részletes információk megtalálhatók a "LOGIC LAB reference" kézikönyvben [15]. Az MPROLOG futtatásához az IBM PC-DOS ismeretét feltételezzük · Egy MPROLOG program futtatása úgy kezdõdik, hogy behívjuk a PDSS-t a C>pdss<CR> parancs segítségével. A < CR > karaktersorozat azt jelöli, hogy a parancs begépelése után le kell ütni a kocsivissza (carriage return) billentyût. Ha az MPROLOG grafikát is szeretnénk használni, akkor a C > pdss /graphic < CR > parancs kiadása szükséges. A PDSS bejelentkezését az alábbi szöveg kiírása jelzi, amely módosulhat a különbözõ verziók esetében: MPROLOG (2.10) LOGIC-LAB (c) 1985 Institute for Coordination of

Computer Techniques (SZKI) PDSS Program Development Support System A szöveg kiírását egy : karakter megjelenése követi a következõ sor elején: : Ez azt jelenti, hogy a PDSS kész fogadni a parancsokat. A parancsok begépelésével a : karaktert természetesen felülírjuk. Most be kell olvasni a programunkat Erre két módszer is van Ha nagyon rövid a programunk, beolvashatjuk állításonként az enter paraiccsal. Például: : enter kaphato(vanilia). < CR > DEFINITION kaphlato/1 STATEMENT 1 az elsõ állítás. PDSS visszaigazolja : enter kaphato(csoki). < CR > DEFINITION k /1 STATEMENT 2 : a második állítás. PDSS visszaigazolja Egy normális hosszúságú programot azonban célszerûbb szövegszerkesztõvel megírni, és beolvastarrni a PDSS-el. A szövegszerkesztõvel írt MPROLOG programokat úgy célszerû elnevezni,hogy file nevét pro kiterjesztés kövesse, pl: fagylalt.pro mert így a PDSS fel fogja ismerni az MPROLOG programokat,és a consult

paranccsal beolvashatjuk. : c fagylalt < CR > A c a consult parancs rövidítése. A c fagylalt hatására a rendszer beolvassa az elsõ példaprogramot, a fagylaltpro file-t A beolvasás közben ellenõrzi, hogy a program megfelel-e a szintaktikus követelményeknek. Ha szintaktikai hibát talál, hibajelzést ad és lehetõséget nyújt a módosításra. A fagylalt program beolvasása közben a következõ jelenik meg a képernyõn: MODULE fagylalt BODY. DEFINITION fagylalt/0 DEFINITION vasarol/1 DEFINITION finom/1 DEFINITION kaphato/1 DEFINITION otthon marad/0 ENDED. fagylalt CONSULTED. : Ha a PDSS beolvasta a programot, adhatunk feladatot a programnak, azaz beüthetjük a megfelelõ célállítást. A célállítást kérdõjellel vagy anélkül is meg lehet adni. Például: ? fagylalt. < CR > célállítás Menj a boltba es vegyel tutti frutti fagylaltot! Yes a program válasza a feladatot sikerült megoldani :? kaphato(eper). < CR > NO : célállítás a

feladatot nem sikerült megoldani A módosított programot a save paranccsal lehet kimenteni. A save parancs rövidítése s Például: :s fagylalt < CR > A PDSS-bõl a bye paranccsal lehet kiszállni: : bye < CR > Normal exit from MPROLOG PDSS C> parancs PDSS válasza, a rendszer visszatér a DOS-ba A PDSS szerkesztõprogram utasításai • Ha a beolvasás közben szintaktikai hibára bukkantunk, vagy más okból módosítani szeretnénk a programot, megtehetjük ezt a PDSS-en belül is a PDSS saját szerkesztõprogramjával. Az editor egy soros szerkesztõprogram, egyszerre egy állítás szerkeszthetõ vele. Az MPROLOG állítás sorait beszámozza, és egy programsorra a sorszámával lehet hivatkozni. A szerkesztõprogramot úgy lehet behívni, hogy az ed beütése után megadjuk annak a definíciónak a nevét, amelyet módosítani akarunk. Például: :ed kaphato < CR > Ha a definícióban több állítás is van, a rendszer meg fogja kérdezni, hogy

az elsõ állításra gondoltunk-e? Igen vagy nem válaszokkal juthatunk el a kérdéses állításhoz. Például: :ed kaphato < CR > edit DEFINITION kaphato/1 STATEMENT 1 (y/n) ? n < CR > edit DEFINITION kaphato/1 STATEMENT 2 (y/n) ? n < CR > edit DEFINITIOÑ kaphato/1 STATEMENT 3 (y/n) ? y < CR > Ez elérhetõ közvetlenül. az ed kaphato + 3 paranccsal is 10: kaphato(malna). a PDSS kiírja a javítandó állítást és * Enter editor commands kéri a szerkesztõ utasítást edit:m 10 < CR > módosítsd a 10-es sort! kaphato (banan). < CR > a módosított állítás * Line 10 replaced edit: e < CR > : a PDSS beteszi az új 10-es sort vége a szerkesztésnek • A szerkesztõ utasítások következõ ismertetésénél a szögletes zárójelpár ([ ]) azt jelenti, hogy a zárójelben lévõ egység alkalmazása nem kötelezõ. A csúcsos zárójelpár (< >) a megfelelõ szintaktikus egység nevét tartalmazza Szerkesztõ

utasítások 1. type [ < sor > ] [ - < sorszám > ] Ha nem használjuk az argumentumokat, a szerkesztõprogram meg fogja jeleníteni az állítás összes sorát. Egyébként csak a kiválasztott sort vagy sorokat. 2. delete < sorszám > [ - < sorszám > ] A PDSS kitörli a megfelelõ sort vagy sor intervallumot. 3. < sorszám >: < szöveg > Hogyha ez létezõ sorszám az állításban, a régi szöveget kicseréli az új szövegre. Ha ez a sorszám új, akkor az új szöveggel bõvül az állítás. 4. modify < sorszám > A PDSS megjeleníti az adott sort a képernyõn. Ezek után használhatjuk a terminál adta lehetõségeket a sor módosítására. < CR > beütésével lehet a kész sort elküldeni 5. ! < PDSS parancs > A PDSS végrehajtja az adott parancsot, majd visszadja a vezérlést a szerkesztõprogramnak. 6. end Ez az utasítás azt jelenti, hogy az állítás módosítása végetért, és ellenõriztetni akarjuk a

rendszerrel, hogy szintaktikusan helyes-e az új állítás. Ha még mindig van hiba, a szerkesztõprogram újra bejelentkezik 7. quit Ez az utasítás azt jelenti, hogy ki akarunk lépni a szerkesztõ programból anélkül, hogy a módosított állításból bármit is megõriztünk volna. Ha az eredeti állítás még mindig hibás, az sem lesz figyelembe véve a program futásakor 8. help A PDSS megjeleníti ezt a kilenc parancsot. 9. = Ez az utasítás újra megjeleníti az utolsó parancsot. Ezt módosítani lehet, és újra elküldeni, mint egy teljesen új parancsot. A fenti utasításokat többnyire rövidítve is meg lehet adni: type -t end -e delete -d quit -q modify - m help -h Nézzünk példákat: 10: vasarol(X) :20: finom(X), 30: olcso(X), 40: kaphato(X). * enter editor commands edit: d 30 < CR > LINE 30 DELETED edit: type < CR > 10: vasarol(X) :20: finom(X), 30: kaphato(X). edit: q < CR > OK : Az elõzõekben kitöröltük a 30-as sort, és kiírattuk

a módosított állítást. A következõ példában beszúrunk egy új sort, módosítunk egy másikat, kinyomtatjuk az állítást és lezárjuk a szerkesztést: 10: vasarol(X) :20: finom(X), 30: olcso(X), 40: kaphato(X). * enter editor commands edit: 35: piros(X), < CR > LINE 35 ADDED edit: m 30 < CR > megfizetheto(X), < CR > LINE 30 REPLACED edit: type < CR > 10: vasarol(X):20: finom(X), 30: megfizetheto(X), 35: piros(X), beszúrunk egy sort módosítás 40: kaphato(X). edit: e < CR > DEFINITION vasarol/1 STATEMENT 1 REPLACED Nyomkövetés • A PDSS-nek interaktív nyomkövetõ rendszere van, amely lehetõséget ad a kiválasztott predikátumok nyomkövetésére, ennek megjelenítésére, be lehet avatkozni a program futásának menetébe és módosítani is lehet a programot nyomkövetés közben. Az interaktív nyomkövetõ rendszer modelljét a következõ ábra mutatja be: 37. ábra Az interaktív nyomkövetõ rendszer modollje A >,

<, +, – szimbólumok jelölik egy predikátumdefiníció nyomkövetésre vonatkozó belépési illetve, kilépési pontjait: Belépés ( > ): a belépési pontra az elsõ kiértékeléskor kerül sor. Sikeres kilépés ( + ): a sikeres kilépés sikeres kiértékelés után történik. Kudarc, kilépés ( – ): a kudarccal járó kilépés a sikertelen kiértékelés után történik. Újra belépés (<): a visszalépés során történik az újra belépés. • Ha az összes definíciót szeretnénk nyomkövetni, a spy * parancsot kell kiadnunk. Ha az összes statikus beépített eljárást szeretnénk nyomkövetni, a spy STATIC standard DEFINITION * parancsot kell beütnünk. A dinamikus beépített eljárásokra a spy DYNAMIC * utasítást kell használni. Természetesen meg lehet nevezni azt a predikátumot, amit nyomkövetni szeretnénk, ilyenkor a * helyére a megfelelõ predikátumnevet kell írni, például: spy kaphato • A nyomkövetési pontokat a nospy parancs

oldja fel. Ha már semmit sem akarunk nyomkövetni, a nospy * parancsot kell kiadnunk, ha egy bizonyos predikátumot szeretnénk feloldani a nyomkövetés alól, a * helyére be kell írni a predikátum nevét, például: nospy kaphato • Nézzük meg egy példán, hogyan is történik a nyomkövetés. A FAGYLALT program néhány predikátumát fogjuk nyomkövetni: module fagyalt. body. fagylalt :vasarol(X), nl, write("Menj a boltba es vegyel "), write(X), write(" fagylaltot !"). fagylalt :otthon marad, nl, write("Maradj otthon, es egyel "), write("olyan fagylaltor, ami van"),nl. vasarol(X) :finom(X), kaphato (X). finom(rum). finom(tutti frutti). finom(csoki). finom(eper). finom(malna). kaphato(vanilia). kaphato(csoki). kaphato(malna). kaphato(tutti frutti). otthon marard. endmod /* fagylalt /. • Bemutatjuk, hogy mi jelenik meg a képernyõn a FAGYLALT program beolvasása és futtatása közben: : c fagylalt < CR > MODULE fagylalt

BODY. DEFINITION fagylalt/0 DEFINITION vasarol/1 DEFINITION finom/1 DEFITION kaphato/1 DEFINITION otthon marad/0 ENDED. fagylalt CONSULTED. : spy vasarol < CR > SPYPOINT added for vasarol/1 : spy finom < CR > SPYPOINT added for finom/1 : spy kaphato < CR > SPYPOINT added for kaphato/1 : ? fagylalt. <CR> * > vasarol( 831) * > finom( 831) * + finom(rum) * > kaphato(rum) * - kaphato(rum) * < finom(rum) * + finom(tutti frutti) * > kaphato(tutti frutti) * + kaphato(tutti frutti) * + vasarol(tutti frutti) Menj a boltba es vegyel tutti frutti fagylaltot! Ýes : nospy * < CR > SPYPOINT removed from vasarol/1 SPYPOINT removed from finom/1 SPYPOINT removed from kaphato/1 Kiszállás a nyomkövetésbõl • Elõfordulhat, hogy egy feladat megoldása közben hibát talál a PDSS, leállítja a futást, és automatikusan beindítja a nyomkövetést. A következõképpen kell kiszállni a nyomkövetésbõl: > valami(X) trace : a < CR > * solution

abandonned nyomkövetés parancs a futás megszakítására PDSS válasza Ha folytatni akarjuk a feladatmegoldását, de nyomkövetés nélkül, a következõképpen kell eljárni: > valami(X) trace: j < CR > Yes nyomkövetés parancs a nyomkövetésbõl való kiszállásra és a futás folytatására • A PDSS lehetõséget ad egy feladat megoldásának lépésenkénti nyomkövetésére is.Ezt úgy érhetjük el,hogy a trace: kiírása után leütjük a < CR > billentyût. Ekkor a végrehajtás egy lépéssel folytatódik, majd a PDSS megint kiírja a trace: üzenetet. Például: > valami (X) trace: < CR > > masik feladat (X) trace: • Két eset is lehetséges, amikor a program olyan hosszasan fut, hogy eléri a beépített híváskorlátot, amely általában 10000, és automatikusan beindul a nyomkövetés. Az elsõ esetben a program végtelen ciklusba keveredett, ilyenkor kiszállhatunk az a paranccsal. A másik esetben mindössze arról van

szó, hogy a programunk elég nagy, és több idõre van szüksége a feladat megoldásához. Ilyenkor célszerû a híváskorlátot kiterjeszteni,és tovább futtatni a programot: > va!ami(X) Error: evaluation limit at valami(X) Limit = 10000 trace: ! set evaluation limit = 50000 < CR > trace: j < CR > nyomkövetés hibaüzenet híváskorlát kiterjesztése parancs a nyomkövetés átugrására IV. AZ ANGOL KIFEJEZÉSEK MAGYAR MEGFELELÕI a accept add after append ascend back before body channel character clear close coded color continue comment current cut delete descend down dynamic else end equal evaluation export external fail file global going graphic heading hidden if infix integer import egy elfogad hozzáad után,utána hozzáfûz növekvõ hát,hátra elõtte törzs,test csatorna karakter letöröl,letisztít lezár kódolt szín folytat kommentár,magyarázat kurrens,folyamatban lévõ levágás törölni csökkenõ le,lent dinamikus egyébként vége

egyenlõ,azonos kiértékelés kivitel külsõ kudarc telep,tömb globális,teljes megy grafikus fejállás rejtett ha belsõ egész szám kivitel input is limit line local main mode module move new no not numeric not off on output page palette pen position prefix random read real record rereadable reset round scale screen screenmode screensize set small state space statement suffix symbolic text tilt than token top turn up value visible write yes bemenõ van határ vonal, sor helyi fõ mód modul mozog új ne, nem nem szám nem ki be kimenõ oldal paletta toll helyzet elöl véletlenszerû olvas valós mondat újra olvasható beállít (alaphelyzetbe) kerek skála képernyõ képernyõ üzemmódja képernyõ mérete beállít (átállît) kicsi állapot, helyzet tér, szóköz állítás, mondat után szimbólikus szöveg billen mint jel,token teteje,legfölül fordul fent érték látható ír igen TARTALOMJEGYZÉK BEVEZETÉS FAGYLALT CSALÁD NAGY CSALÁD TERV ÖSSZEG

TÉGLALAP RAJZ SZÍNEZ LISTA ÉPÜLET KOCKA LAKÁS UTÓSZÓ IRODALOMJEGYZÉK I. FÜGGELÉK II.FÜGGELÉK III.FÜGGELÉK IV.FÜGGELÉK . PROLOG programok alapelemei, tényállítások,szabályok,definíciók,célállítások. Egy cél végrehajtása. Mintaillesztés, visszalépés Különbözõ típusú definíciók. Rekurzivitás Bemenõ,kimenõ paraméterek Duplikáció kiküszöbölése,"cut".Tagadás a PROLOG-ban Egy egyszerû építészeti tervezõ program.Specifikáció és a program egysége Ciklus szervezése rekurzióval.Aritmetika . Összetett kifejezések 1., prefix operátorok Grafika a PROLOG-ban. . Kérdés-válasz,menübõl való választás. . Összetett kifejezések 2.,listák Rekurzió listákon Összetett kifejezések 3.,infix operátorok Az operátorok prioritása Dinamikus beépített eljárások. A végtelen ciklus elkerülése Moduláris programozás.Modulok közötti kapcsolat Globális és lokális változók . . Megoldasok a gyakorlatokra .

IBM PC MPROLOG beépített eljárásai . MPROLOG használata IBM PC környezetben . Az Angol kifejezések magyar megfelelõi . 1 2 9 16 23 28 34 38 44 49 55 60 66 72 73 74 89 92 98 And at last: the contents of this file was scaned and "recognized" by Jack Clement, edited and spelled by Calden in the year of 1996