Amikor tanulmányozzuk, hogy milyen témákat használnak a prológus nyelve. Logikai programozás. A Prolog nyelv alapjai. Programkód kérések nélkül

És fontolja meg a megoldásokat a Prolog legegyszerűbb problémáira.

Mielőtt elkezdenénk, szeretnék röviden válaszolni olvasóink sürgető kérdéseire:
- Hol használják valójában a Prolog-ot?
- Léteznek ilyen projektek, néhányat az 1. cikkhez fűzött megjegyzésekben idéztek. Fontos, hogy a legtöbb programozó ne reménytelenségből írjon Prologba, hanem azért, mert szereti a Prologot. Végül is a Prolog nem használható semmilyen feladatra, például felhasználói felület létrehozására vagy fájlok manipulálására.

Miért van kevés ilyen projekt?
- Mert nagyon kevés programozó ismeri a Prologot, nemcsak azért, mert az emberek nem tanulták, hanem azért, mert nem tanulták annyira, hogy komplett programokat írjanak. Ennek fő oka az, hogy az emberek nem értik egyértelműen, milyen helyzetekben a legjobb használni. Gyakran látni, hogy a Prolog lelkes támogatói mindent beleírnak, beleértve a billentyűzet- és egérkezelőket is, ezért a kód még rosszabbul is sikerül, mint a C-ben.

Miért nincs Prolog közösség?
- Ez. A nyelv sajátossága, hogy nagyon népszerű az akadémiai környezetben (a legtöbb Prolog rendszert különböző egyetemeken írják, és éppen ellenkezőleg, szinte minden egyetem ír saját Prologot), emiatt elmondható, hogy az alkalmazhatóság a nyelv szenved. Érdemes megjegyezni, hogy a közösség kicsi, de nagyon lojális: szinte minden ismert nyelv tükröződik a modern nyelvekben (Lisp, ML -> F#, Scala; Smalltalk -> Java, Scala (ügynökök), scripting -> Ruby), ellentétben a Prologue-val.

Szerintem ennyi filozófiai érvelésnek elég, és kezdhetjük is a valós példákkal :)

A végén szokás szerint jutalomért járó feladat vár.

1. példa – tökéletes számok keresése

Ehhez a példához szükségünk lesz az is/2 predikátumra. X értéke 3 + 1 * 2- kiszámolja a jobb oldali kifejezést és a bal oldali változóba teszi, ez nem hozzárendelés (!), hanem egy állítás, hogy X = 7. Egyszerűen fogalmazva az X = 7, X = 3 kifejezésnek nincs megoldása, mert X nem lehet 7 és 3 egyszerre.
A probléma megoldására is szükségünk van az előző témakörből. A feladat egy olyan predikátum felírása volt, amely az összes természetes számot generálja egymás után, itt a megoldás:
ints(0). ints(X) :- ints(Y), X jelentése Y + 1.
Ez valójában a standard integer/1 predikátum deklaratív változata, amely ellenőrzi, hogy az argumentum egész szám-e. A probléma a szabványos predikátummal az, hogy megfelelően működik a lekérdezés:- integer(1) esetén, és nem működik az integer(X) lekérdezésnél.

Feladat: írjon egy programot, amely minden tökéletes számot megtalál.
A megoldás kézenfekvő, végigfuttatjuk az összes egész számot, és ellenőrizzük, hogy tökéletesek-e, ez a stratégia nagyon jól alkalmazható imperatív nyelvekre, mi magunk sem vesszük észre, hogy azonnal keresünk egy algoritmust a megoldás megtalálásához, ahelyett, hogy elemeznénk a probléma. A Prologban ne a probléma megoldásának keresését próbáljuk leírni, hanem a probléma megfogalmazását próbáljuk meg leírni, kövesse a szabályt:

Ne próbáljon leírni a megoldás megtalálására vonatkozó utasításokat, feltételezze, hogy már megtalálta a megoldást, és az Ön feladata csak az, hogy ellenőrizze, hogy sikerült-e megoldást találni.

Furcsa módon ez a stratégia remekül működik.
%% Természetes számok deklaratív definíciója int(0). ints(X) :- ints(Y), X értéke Y + 1. %% Egy tökéletes szám 1) természetes szám 2) az osztók összege egyenlő a tökéletes_szám(X) számmal :- ints(X) , Y X - 1, számítja_osztók_összege_ig(Sum, X, Y), Sum = X. %% Osztók összegének ellenőrzése 1. argumentum Sum, 2nd - az a szám, amelyre osztókat keresünk, %% 3. - a szám felfelé amelyhez osztókat keresünk számítássum_osztók_till(0, _Osztószám , 0). számítja_osztók_összege_ig(összeg, osztandó szám, mig) :- Till > 0, Rem = Osztandó szám mód Meddig, Rem = 0, Ts Mig - 1, számítja_osztók_összege_ig(Összeg, Osztandó Szám, Ts), Összeg: Előző összeg + Míg. számítja_osztók_összege_ig(összeg, osztandó szám, mig) :- Till > 0, Rem = Osztandó szám mód Meddig, Rem > 0, Ts = Mig - 1, osztók_összege_ig(összeg, Osztandó szám, Ts).

A forrásszöveget beszúrjuk a fájlba, elindítjuk az interpretert és lefordítjuk (a kéréssel: -compile("fájl_elérési_útja/tökéletes_számok.pl") Írjuk meg a kérést :- tökéletes_szám(X).és a tolmács választ ad, amikor megnyomja a ";" gombot. a következőket adja. Felhívjuk figyelmét, hogy a kérés lehet :- tökéletes_szám(X), X > 6. Ekkor minden válasz nagyobb lesz 6-nál. Természetesen a program nem működik optimálisan, maga a teszt egyszerű osztókkal leegyszerűsíthető, próbáld ki.

2. példa – permutációk generálása.

A probléma felvetéséhez és megoldásához szükségünk lesz a listák fogalmára. A listák nem az alapfogalmak a nyelvnek, közvetlen analógia vonható a listák között a linkelt listákkal a C-ben. Térjünk vissza a kifejezés mint rekurzív adatstruktúra meghatározásához.
%% Határozzon meg egy üres listát nil list(nil) objektumként. %% Határozzuk meg egy elemből álló listát 1 list(t(1, nil)). %% Adja meg az 1, 2, 3 elemek listáját list(t(1, t(2, t(3, nil)))). %% Leírjuk például a keresési eljárást a listában %% 1. az eredmény a lista fejében van (1. elem) %% _ - jelentéktelen változót jelent számunkra tag(X, t(Y, _ )) :- X = Y. %% 2. az eredmény nem az első elem, hanem az első elem után a lista végében szerepel tag(X, t(_, Tail)) :- tag(X, Farok).

Ahogy sokan mondanák, a közönséges rekurzió, és hogy a listák ne tűnjenek különösebbnek a Prologban, van rájuk szintaktikai cukor: nil írható , t(1, nil) - , t(1, t(2, nil)) - , t( 1, Allista) - , t(1, t(2, Allista)) - . Listákhoz javasolt a szintaktikai cukrot használni, mert a kifejezések belső nevei eltérőek lehetnek (leggyakrabban a kifejezést "."-nek hívják).
%% 1. az eredmény a lista élén áll (1. elem) tag(X, ). %% 2. az eredmény nem az első elem, hanem az első elem után a lista végében szerepel tag(X, [_| Tail]) :- member(X, Tail).
Térjünk vissza a permutációk generálásának eredeti problémájához. Mindenki tökéletesen emlékszik arra, hogy a permutációk száma n!, de adja ezt a feladatot a programozók többségének, és mindenki eszeveszetten fog emlékezni, és azt mondja, hogy ezt az iskolában írták, és elfelejtették, hogyan kell keresni. Átlagosan kb 20 perc alatt megjelenik az algoritmus erőlködés és kínlódás után, ha ismeri a Prolog-ot, ezt az algoritmust 2 perc alatt meg lehet írni, vagy meg sem lehet írni :)

Hogyan lehet megoldani a Prologban? Használjuk azt a szabályt, hogy nem találunk megoldást, hanem ellenőrizzük, hogy sikerült-e megoldást találni. Állítmány perm (forrás, permutáció)- ahol a Forrás az eredeti lista, a Permutáció egy permutáció.

%% Ha az eredeti lista üres, akkor az üres lista perm(, ) egy permutációja van. %% A permutáció 1. elemének szerepelnie kell a forráslistában, %% és azonnal ki kell zárni az eredeti listából, %% a fennmaradó elemeknek a %% fennmaradó eredeti lista perm(Forrás, ) permutációinak kell lenniük. :- tag_list_exclude(Element, Source , SourceExcluded), perm(SourceExcluded, Tail). %% Annak ellenőrzése, hogy az elem szerepel-e a listában, és a 2. lista elem nélküli lista %% A tag_list_exclude predikátum neve megegyezik az argumentumok sorrendjével %% Az 1. egy elem, a 2. egy lista, a 3. egy lista tagok nélkül tag_list_exclude (X, , L). tag_list_exclude(X, , ) :- tag_list_exclude(X, L, Ls).
Kérés :-perm(, X) minden permutációt generál. Érdekes módon a lekérdezések szimmetrikusak :-perm(X, ) az érvekkel kapcsolatban, bár ez a kérés lefagy, és a működéséhez fel kell cserélni a member_list_exclude és a perm értéket a perm-ben.

3. példa – kombinációk generálása.

A végrehajtás egyszerűsége szempontjából a kombinációk generálása hasonló a permutációk generálásához. Szükségünk van a predikátum tagra/2 - az elem a listához tartozik. Tegyük fel, hogy 2 listánk van: 1. az eredeti lista, 2. a szándékolt kombináció, ellenőriznünk kell a kombináció helyességét. A kombinációs elemek az eredeti lista sorrendjében vannak elrendezve.

Tag(X, ). tag(X, [_|L]) :- tag(X, L). kombinál(, ). %% 1. lehetőség: A kombináció 1. elemét az eredeti lista comb(, ) :- comb(Lista, Tail) tartalmazza. %% 2. lehetőség: a kombináció a lista vége, %% érvényes kombinációja, azaz az eredeti lista 1. eleme nem szerepel a comb([_|List], Tail) kombinációban :- comb( Lista, farok).

4. számú példa – válogatás.

Tekintsük ezt a példát részletesebben, és próbáljuk meg optimalizálni az elsődleges megoldást. A Prologban való írás folyamata a következő: 1) a probléma kezdeti leírása és a kimerítő megoldás megszerzése 2) logikai optimalizálás a predikátumok jobb oldali átrendezésével 3) az egyszerűsített ellenőrzések bevezetésének vagy a szükségtelen feltételek eltávolításának logikai optimalizálása 4) a heurisztika bevezetése és az optimalizálás egyedi esetek vágással.

1. lehetőség. Naiv rendezés: A rendezett tömb első elemének minimálisnak kell lennie, a többi elemnek rendezettnek kell lennie. Az első tömb az eredeti, a második a rendezett.

Rendezés(, ). sort(Lista, ) :- min_list_exclude(Min, List, Exclude), sort(Exclude, SortRest). %% A minimális számot rekurzívan kizárjuk, ha egy szám van a listában, akkor kizárjuk min_list_exclude(M, [M], ). min_list_exclude(Min, , ExcludeRes) :- min_list_exclude(Ms, L, Exclude), find_result(result(M, L), result(Ms, ), result(Min, ExcludeRes)). %% További predikátum egy minimális kulcsú pár meghatározásához find_result(result(M, L), result(Ms, _), result(M, L)):- M< Ms. find_result(result(M, _), result(Ms, Exclude), result(Ms, Exclude)):- Ms =< M.
Látható, hogy ennek az algoritmusnak a bonyolultsága négyzetes, és a fő probléma az, hogy minden alkalommal a minimális elemet keressük anélkül, hogy bármilyen hasznos információt tárolnánk.
Figyeljük meg azt is, hogy megpróbáljuk meghatározni, hogy mi a rendezett tömb 1. eleme.

2. lehetőség. Gyors rendezés. Nézzük meg a problémát a második oldalról, és próbáljuk meg meghatározni a lista 1. elemének helyét a rendezett tömbben (alkalmazzuk a rekurziót az eredeti tömbre).

Rendezés_b(, ). sort_b(, Lista) :- split(T, R, Less, Great), rendezés_b(Kisebb, Kevésbé rendezés), sort_b(Nagyszerű, Nagyszerű rendezés), append(Kisebb rendezés, , Lista). %% A tömböt 2 nagyobb és kisebb tömbre osztjuk szét (_, ,, ). split(T, ,, Nagyszerű) :- M< T, split(T,R, Less,Great). split(T, ,Less, ) :- M >= T, osztás(T,R, Kevesebb, Nagy). %% Ragass össze 2 listát append(, M, M). append(, Right, ) :- append(Bal, Jobb, Res).
Látható, hogy javítottunk a rendezési eredményeken, mivel a gyors rendezés nyilvánvalóan gyorsabb, mint a buborékos rendezés. Az eredmények további javítása érdekében felidézhetjük az egyesítési rendezést, ami mindenképpen O(n lg n-t) ad, de sajnos ez a rendezés csak a tömbökre vonatkozik, azokra a linkelt listákra, amelyekkel dolgozunk, nem. Az egyetlen lehetőség, hogy további adatstruktúrát használjunk a tároláshoz, egy fa.

3. lehetőség: Rendezés bináris fa használatával.

Az ilyen típusú rendezéshez az eredeti listát bináris fává alakítjuk, majd a fa bal oldali bejárásával egy rendezett tömböt kapunk. A fát rekurzív taggal fogjuk ábrázolni fa (Object, LeftSubTree, RightSubTree).
sort_tree(, nulla). sort_tree(, Fa) :- rendezési_fa(L, LTfa), plusz(X, LTfa, Fa). %% Egy elem hozzáadása a fához plusz(X, nil, tree(X, nil, nil)). plusz(X, fa(O, L, R), fa(O, ResL, R)) :- O >= X, plusz(X, L, ResL). plusz(X, fa(O, L, R), fa(O, L, ResR)) :- O< X, plus(X, R, ResR). sort_t(X, Y) :- sort_tree(X, Tree), tree_list(Tree, Y). append_list(, L, L). append_list(, R, ) :- append_list(L, R, T). tree_list(nil, ). tree_list(tree(X, L, R), List) :- tree_list(L, ListL), tree_list(R, ListR), append_list(ListL, , List).

4. lehetőség: Rendezés kiegyensúlyozott bináris fa használatával.

A bináris fa használatának problémája ugyanaz, mint a gyorsrendezés használatával. A módszer nem garantálja az optimális teljesítményt. Egy bináris fa esetében előfordulhat, hogy a fa kiegyensúlyozatlan, és az elem hozzáadásának eljárása inkább lineáris, mint logaritmikus. A fakiegyenlítési eljárásokat kifejezetten erre a célra hajtjuk végre.

Sort_btree(X, Y) :- sort_tree(X, Tree), tree_list(Tree, Y). tree_list(nil, ). fa_lista(fa(X, L, R, _), Lista) :- fa_lista(L, ListaL), fa_lista(R, ListaR), append(ListaL, , Lista). sort_tree(, nulla). sort_tree(, Fa) :- rendezési_fa(L, LTfa), plusz_fa(X, LTfa, Fa). construct_tree(A, AL, AR, fa(A, AL, AR, ADmélység)) :- diff(AL, AR, _, ADmélység). diff(AL, AR, ADiff, ADepth) :- mélység_fa(ALs, AL), mélység_fa(ARs, AR), ADiff ALs - ARs, max_int(ALs, ARs, AD), ADmélység AD + 1. max_int(A , B, A) :- A > B. max_int(A, B, B) :- A =< B. append(, L, L). append(, R, ) :- append(L, R, T). depth_tree(0, nil). depth_tree(X, tree(_, _, _, X)). plus_tree(X, nil, tree(X, nil, nil, 1)). plus_tree(X, tree(O, L, R, _), Res) :- O >= X, plusz_fa(X, L, ResL), diff(ResL, R, Diff, Dep), egyensúlyfa(fa(O, ResL, R, Dep), Diff, Res). plusz_fa(X, fa(O, L, R, _), Res) :- O< X, plus_tree(X, R, ResR), diff(L, ResR, Diff, Dep), balance_tree(tree(O, L, ResR, Dep), Diff, Res). %% No rotations balance_tree(Tree, ADiff, Tree) :- ADiff < 2, ADiff >-2. %% Kis jobbra forgás egyensúlyfa(fa(A, fa(B, BL, BR, _), AR, _), ADiff, eredmény) :- ADiff > 1, diff(BL, BR, BDiff, _), BDiff > = 0, construct_tree(A, BR, AR, ASubTree), construct_tree(B, BL, ASubTree, Eredmény). %% Nagy jobb forgás egyensúlyfa(fa(A, fa(B, BL, BR, _), AR, _), ADiff, eredmény) :- ADiff > 1, diff(BL, BR, BDiff, _), BDiff< 0, BR = tree(C, CL, CR, _), construct_tree(B, BL, CL, BSubTree), construct_tree(A, CR, AR, ASubTree), construct_tree(C, BSubTree, ASubTree, Result). %% Small left rotation balance_tree(tree(A, AL, tree(B, BL, BR, _), _), ADiff, Result) :- ADiff < -1, diff(BL, BR, BDiff, _), BDiff =< 0, construct_tree(A, AL, BL, ASubTree), construct_tree(B, ASubTree, BR, Result). %% Big left rotation balance_tree(tree(A, AL, tree(B, BL, BR, _), _), ADiff, Result) :- ADiff < -1, diff(BL, BR, BDiff, _), BDiff >0, BL = fa(C, CL, CR, _), construct_fa(B, CR, BR, BSubTree), construct_tree(A, AL, CL, ASubTree), construct_tree(C, ASubTree, BSubTree, Result).
Ez a példa nem elég kifejező ahhoz, hogy a Prologban megvalósítható legyen, bár képet ad a közepes méretű programokról. A képzéshez megvalósíthat buborékos rendezést vagy beszúrásos rendezést, ezt az olvasó belátására bízzuk.

5. példa – Transzfúziós probléma.

Következő problémánk esetében tekintsük a klasszikus állapotproblémát, amely sokkal jobban tükrözi a Prolog használatának előnyeit. A probléma általános megfogalmazása: adott edényben, ahol víz van, bizonyos mennyiségű vizet kell öntéssel előállítani egy adott tartályban. Vegyünk például 3 12 literes, 8 literes, 5 literes kancsót, töltsük meg az elsőt teljesen, azaz 12 literrel, és állítsuk be a feladatot. 6 liter. Először próbálja meg megoldani ezt az iskolai problémát egy tollal és egy papírral :)

Mielőtt különféle algoritmusokat generálnánk és megpróbálnánk alkalmazni őket a problémára, először írjuk át a feltételeket Prolog kifejezésekre. Leírjuk a kapacitást kifejezésként sosud(Id, Maximum Capacity, CurrentCapacity), a rendszer állapotát a kapacitások listájaként írjuk le. Példa . Most pedig írjuk le a kérést:

%% solve_pr_wo(kezdeti állapot, cél, lépések). :- solve_pr_wo(, sosud(X, _, 6), Lépések).

Kérjük, vegye figyelembe, hogy a cél = sosud(_, _, 6), vagyis számunkra nem mindegy, hogy mekkora az edény űrtartalma, a lényeg, hogy pontosan 6 liter legyen.

Most, hogy mindent tudunk, írjuk le a módszert ellenőrzi megoldásokat, feltételezve, hogy a lépések a Steps változóban vannak megadva.

%% Nincs szükség lépésre az állapot megszerzéséhez, a %% azt jelenti, hogy az egyik edény szerepel a listában solve_pr_wo(State, Goal, ) :- member(Goal, State). %% Az első lépés a Sosudból a Sosud2-be önteni, és megkapni az első ResSosud és a második ResSosud2 edény %% állapotát. %% Konkrét példa egy lépésre: %% mv(sosud(1, 12, 12) -> sosud(2, 8, 0), sosud(1, 12, 4) -> sosud(2, 8, 8)) . solve_pr_wo(State, Goal, ) :- %% Mindenekelőtt a tartomány ellenőrzése, hogy az edények ténylegesen %% vannak-e az aktuális állapotban, és nem egyenlőek egymással tag(Sosud, State), tag(Sosud2, State) ), not(Sosud = Sosud2), %% Maga a transzfúzió végrehajtása, itt %% mind a 4 lépésváltozó érintett mv(Sosud, Sosud2, ResSosud, ResSosud2), %% Az erek állapotának cseréje az állapotok listájában viszont csere(Sosud, Állapot, ResSosud, Állapot2), csere (Sosud2, Állapot2, ResSosud2, ÁllapotX), %% A további lépéseket a solve_pr_wo(StateX, Goal, Steps) rekurzió segítségével kell végrehajtani. %% Valójában a listában egy elem szokásos cseréje a %% csere(ElementToReplace, InList, ElementToReplaceWith, OutList). cserélje ki (S, , X, ). csere(S, , X, ) :- csere(S, L, X, Ls). %% Transzfúziós eljárás - 2 lehetőség %% a forrásedény kimerül, vagy a cél ér megtelik %% Az első ér ürítése a második mv-be (sosud(Id1, Max1, Current), sosud(Id2, Max2, Current2) , sosud(Id1, Max1, 0 ), sosud(Id2, Max2, Current3)) :- Aktuális >< Max2. %% Переливание из первого сосуда до краев второго mv(sosud(Id1, Max1, Current), sosud(Id2, Max2, Current2), sosud(Id1, Max1, Current3), sosud(Id2, Max2, Max2)) :- Current >0, Aktuális3: Aktuális2 + Aktuális - Max2, Aktuális3 >= 0.

Hozzátéve, úgy tűnhet, hogy a domain ellenőrzése nem szükséges, mert ha a transzfúzió lépései helyesek, akkor nem kell ellenőrizni, hogy mit írnak le. Valójában az ellenőrzés teljessége jelentősen javítja a program helyes működésének esélyeit. Helyesebb lenne ezt mondani: túlzott ellenőrzéssel a program működik, néha még optimalizáltabban, mint anélkül, de elégtelen ellenőrzés esetén a program teljesen hibás eredményeket produkál, vagy lefagy néhány bemeneti adatnál.

Nos, a program leírása meg van írva - futtathatja. Ne csodálkozz, hogy a program nem működik, egyszerűen lefagy :) Ez nem olyan rossz, mint amilyennek tűnhet, mert ha nem fagyott volna le a program, akkor a helyes választ adta volna. Ki kell találnunk, hogy miért fagyott le, és itt a segítségünkre lesz annak megértése, hogy a Prolog hogyan fejti ki a szabályokat a megoldás megtalálása érdekében. Valójában nem kell olyan fejjel rendelkeznie, amely képes megjegyezni akár 10 visszatérési pontot, hogy megértse, hogy minden következő alkalommal, amikor a solve_pr_wo -> rekurzívan hívja a solve_pr_wo-t, akkor 2 tag/2 predikátumot hív meg, amelyek mindig ugyanazt az 1. és 2. eret állítják elő. (a nem predikátum visszalépést okoz, és nem teszi lehetővé a tag számára az 1. és 1. ér kiválasztását). Vagyis az algoritmus folyamatosan 1-től 2-ig és vissza.

Ennek az abszurditásnak a feloldása érdekében rögtön az jut eszembe, hogy meg kell tiltani ugyanazt a cselekvést kétszer, vagyis az állapotok történetét, és ha az állapottal már találkoztunk, akkor megtiltani, hogy ez megismétlődjön. Kiderült, hogy leszűkítjük az elfogadható transzfúziós stratégiák körét, kizárva az ismétlést. Valójában a stratégiák halmazának szűkítésével nem szűkítjük a rendszer megengedhető állapotainak, azaz megoldásainak halmazát, amit nem nehéz bizonyítani.

A program teljes verziója állapotok kinyomtatásával és egyetlen predikátummal a megoldás hívásához:

Write_list(). write_list() :- writeln(X), write_list(L). megoldás:- solve_pr(, sosud(_, _, 6), , Steps), write_list(Steps). cserélje ki (S, , X, ). csere(S, , X, ) :- csere(S, L, X, Ls). %% a transzfúziós stratégiát nem magukat a lépéseket fogjuk figyelembe venni, hanem egyszerűen a végállapotokat %% a kezdeti és végállapot ismeretében nem nehéz kitalálni, melyik lépést hajtották végre solve_pr(State, Goal, _, ) :- member( Cél, állapot). solve_pr(állapot, cél, előzmények, ) :- tag(Sosud, State), tag(Sosud2, State), nem(Sosud = Sosud2), mv(Sosud, Sosud2, ResSosud, ResSosud2), helyettesít(Sosud, State, ResSosud , Állapot2), csere(Sosud2, Állapot2, ResSosud2, ÁllapotX), %%% ugyanaz a végső állapot ellenőrzése not(member(StateX, )), solve_pr(StateX, Goal, , Steps). %% mv(sosud(_Id, Max, Current), sosud(_Id2, Max2, Current2), ...,...). %% Az első edény ürítése a második mv-ben(sosud(Id1, Max1, Current), sosud(Id2, Max2, Current2), sosud(Id1, Max1, 0), sosud(Id2, Max2, Current3)) :- Current > 0 , Aktuális3 az Aktuális2 + Aktuális, Aktuális3 =< Max2. %% Переливание из первого сосуда до краев второго mv(sosud(Id1, Max1, Current), sosud(Id2, Max2, Current2), sosud(Id1, Max1, Current3), sosud(Id2, Max2, Max2)) :- Current >0, Aktuális3: Aktuális2 + Aktuális - Max2, Aktuális3 >= 0.

Most minden működik! Gyakorlatként módosíthatja a programot úgy, hogy az optimális lépésszámban találja meg a transzfúziót. Ezekkel a problémákkal kísérletezhet.

Megjegyzés: az imperatív programozás lelkes támogatói észreveszik majd, hogy mi csak annyit tettünk, hogy átvertük az összes visszatérési állapotot (mélység-első átjárás), heurisztika használata nélkül, és teljesen igazuk lesz. A helyzet az, hogy a Prologban nem nyers erőből kell gondolkodni, hanem a probléma leírásával és a megoldás ellenőrzésének leírásával, és mindig vissza kell térni az optimalizáláshoz szükséges számítások kötelezőségéhez, ha szükséges! A természet kettőssége nem mínusz, hanem plusz. Azt is érdemes megjegyezni, hogy a nagy Prolog rendszerek nagyon jól alkalmazkodnak a hozamú állapotok megtalálásához.

Következtetés

Szeretném megjegyezni, hogy a cikkben tárgyalt problémák Prolog programozási tanulmányok. Mivel a legtöbb körülbelül 10-15 soros, a Prolog programozó képes ezeket a memóriából reprodukálni, ha elég gyakran átnézik. És mindenképpen érdemes visszatérni hozzájuk, mert a programozás művészetére emlékeztet (akárcsak a gyors válogatás C-ben). A mindennapi használatra szánt összetettebb és alkalmazottabb feladatokról később lesz szó.

A végén 2 rejtvény van egy nyereményért:

  1. Mint ismeretes, funkcionálisan és logikailag minden lehetséges módon igyekeznek elkerülni a mellékhatásokkal járó programokat, monádokba csomagolni, speciális koncepciókat kitalálni. A standard probléma egy külső világ problémája, például az adatok fájlba írása lehetetlen visszaállítani a fájlba írást, vagy megszakítani több bájt egy socketen keresztüli küldését, és ezért a visszalépés nem működik teljesen. Csak egy tanács van: ne használja a Prolog-ot ilyen célokra. De vannak olyan predikátumok, amelyek nagyon jók és specifikusak a Prologra, de van mellékhatásuk. Példa állítás (asserta, assertz): egy egyszerű szabályt (tényt) ad a szabály (tény) alapjához. Példa érvényesít(prím(3)): hozzáteszi, hogy a 3 prímszám és lekérdezés :-prím(X), most a 3-at fogja kiírni, még akkor is, ha az eredeti program üres.

    Feladat: írjon kijelentő változatot állítja, vagyis amikor a backtracking program visszatér, a ténynek nem szabad a memóriában maradnia, hanem logikai találgatásként kell működnie.

    Példa a munkára: A c(X) lekérdezésnek egy 4-es számot kell produkálnia a következő programhoz!
    a(X) :- b(Y), X jelentése Y+1. c(X) :- saját_állítás(b(3)), a(X). c(X) :- b(X).

  2. A klasszikus matematikai logikában 2 elmélet sokkal nagyobb figyelmet kap, mint az összes többi - ez halmazelméletÉs predikátumelmélet. Van köztük egy bizonyos kapcsolat, az egyik a másikon keresztül fejeződik ki, és fordítva. Például egy predikátum olyan értékek halmaza, amelyekre igaz, és fordítva, a halmaz egy tagsági predikátum. A hagyományos relációs adatbázis-elmélet halmazokkal, míg a Prolog predikátumokkal operál. Általában a feladat egy, a halmazelmélet számára abszolút hagyományos művelet kifejezése. az összes részhalmaz halmazának felvételének művelete.

    Feladat: adott valamilyen unáris predikátum a/1 (általános esetben az elemek halmaza nem korlátozott, lehet végtelen), írjunk egy subset_a/1 predikátumot, amely az a halmaz elemeiből álló részhalmazokat fog előállítani.

    Példa: a subset_a(X) lekérdezés a következőt adja vissza: X = , X = , X = , X = (a sorrend nem fontos):
    a(1). a(2). subset_a(X) :- ....?

Köszönöm a figyelmet.

Címkék: Címkék hozzáadása

Prolog programozási nyelv az egyik vezető logikai programozási nyelv. Alain Colmerauer készítette az 1970-es években. Kísérlet volt egy olyan kezdőknek szóló programozási nyelv létrehozása, amely lehetővé teszi a logika kifejezését, ahelyett, hogy a számítógép képernyőjén megjelenő utasításokkal gondosan meghatározná, mit szeretne elérni.

A Prologot számos mesterséges intelligencia programban használják, de szintaxisa és szemantikája nagyon egyszerű és áttekinthető (az eredeti cél az volt, hogy eszközt biztosítson a számítógépes analfabéta nyelvészek számára). A Prolog név a LOGIC PROGRAMMING rövidítése, és széles körben ismert a programozás alapjainak oktatásában.

A Prolog predikátumszámításon (pontosabban elsőrendű predikátumszámításon) alapul, de a Horn-képletekre korlátozódik. A Prolog programok hatékonyak a tételek első közelítésre való bizonyítására. Az összekapcsolás, a farokrekurzió és a követés alapfogalmai.

Adattípusok

A Prolog nem úgy használja az adattípusokat, ahogyan azt az általános programozási nyelvekben megszokhattuk. Az adattípusok helyett a Prolog lexikai elemekről beszélhetünk, ami szokatlan a dumi programozásban.

A konstans szöveg bevitele atomok használatával történik. Az atom betűkből, számokból és aláhúzásból álló sorozat, amely kisbetűvel kezdődik. Általában, ha ezek nem alfanumerikus atomok, akkor ezeket aposztrófokkal kell keretezni (például a "+" egy atom, a + egy operátor).

A legtöbb Prolog implementáció nem tesz különbséget valós és tört számok között.

Változók

A változókat egy betűkből, számokból és aláhúzásjelekből álló karakterlánc azonosítja, amely nagybetűvel kezdődik. A Prolog környezetben a változó nem hozzárendelhető tároló (ellentétben a procedurális programozási nyelvekkel). Viselkedése közelebb áll az összekapcsoláshoz hasonló modellhez.

Az úgynevezett anonim változókat egyetlen aláhúzásjelként (_) írjuk.

A Prolog az egyetlen módja annak, hogy összetett adatokat jelenítsen meg. A kifejezés egy fejből, más néven funktorból (amelynek atomnak kell lennie) és paraméterekből (nem korlátozódik a típusokra) áll. Fontos a paraméterek száma, a kifejezés úgynevezett aritásai. A kifejezést a feje és az aritása határozza meg, általában functor/arityként írják le.
Listák

A lista nem önálló adattípus, mert rekurzív konstrukció határozza meg (a /2 "." kifejezést használva):

Atom – üres lista

A programozó kényelmét szolgálja, hogy a listák többféleképpen is összeállíthatók és megsemmisíthetők.

Ha L egy lista és X egy elem, akkor "." (X, L) a lista tagja. Az első X elemet, amelyet az L kontextus tartalma követ, szintaktikailag a következőképpen ábrázoljuk.

A programozó kényelmét szolgálja, hogy a listák többféleképpen is összeállíthatók és megsemmisíthetők.

Tétel lista:
Egy elem előszava:
Előzetesen több elem:
Kifejezés kiterjesztés: "."(abc, "."(1, "."(f(x), "."Y, "."."(g(A,rst), )))))

A karakterláncokat általában idézőjelekben szereplő karakterek sorozataként írják. Gyakran ASCII karakterkódok listájaként jelennek meg.

A PROLOG programozása nagyon különbözik a procedurális nyelvekkel való munkavégzéstől. A Prologban tényeket és szabályokat tartalmazó adatbázisokkal dolgozik, adatbázis-lekérdezéseket futtathat. A Prolog alapegysége egy igaznak definiált predikátum. Egy predikátum egy fejből és számos argumentumból áll. Például:

Itt a "macska" a fej és a "tom" az érv. Íme néhány példalekérdezés, amelyet a Prolog fordító segítségével futtathat ezen tény alapján:

Cat(tom).
Igen.

Cat(X).
X = tom;
nem.

A predikátumokat általában úgy határozzák meg, hogy kifejezzék azt a tényt, amellyel a program ismeri a világot. A legtöbb esetben a predikátumok használata bizonyos konvenciókat igényel. Tehát az alábbiak közül melyik jelenti azt, hogy Pat Sally apja?

Apa (sally, pat).
apa (pat, Sally).

Az "apa" mindkét esetben a fej, a "sally" és a "pat" pedig az érvek. Azonban az első esetben Sally az első helyen áll az érvek listáján, Pat pedig a második helyen és fordítva. Az első eset egy példa egy definícióra Ige Tárgy és Tárgy sorrendben, de a második példában a sorrend az Ige Tárgytárgy. Mivel a Prolog nem ért angolul, mindkét verzió rendben van, de jó programozási stílusnak tekinthető, ha egy program írásakor ragaszkodunk egy stílushoz, amit konvenciónak neveznek, ahelyett, hogy valami ilyesmit kellene írni:

Apa (pat, sally). apa (Jessica, James).

Bizonyos predikátumok be vannak építve a nyelvbe, és lehetővé teszik a Prolog számára, hogy csökkentse a mindennapi tevékenységek fáradalmait (például grafikus bemenet/kimenet és egyéb kommunikáció az operációs rendszerrel). Például az írási predikátumok a következő megjelenítéshez használhatók:

Írj ("hello")

A „Hello” szó jelenik meg a képernyőn.

Szabályok

A Prolog második típusú utasítása a szabályok. Példaszabály:

Világítás (be) :- kapcsoló (be).

A ":-" azt jelenti, hogy "ha", ez a szabály azt jelenti, hogy a fény (be) igaz (be), ha a kapcsoló (be) igaz (egyébként ha a kapcsoló be van kapcsolva, akkor van lámpa). A szabályok változókat is használhatnak, míg a konstansok kisbetűkkel kezdődnek. Például,

Apa(X,Y):- szülő(X,Y),férfi(Y).

Ez azt jelenti, hogy "ha valaki szülő és férfi, akkor apa." Az okok és következmények fordított sorrendben is lehetnek, tehát ez nem mond ellent a hétköznapi logikának. Több predikátumot is elhelyezhet egy következményszakaszban, ha kombinálja őket, például:

Ami több deklarációnak felel meg:

A:- d. b:- d. c:- d.

De olyan utasítások, mint:

Ami a "ha c, akkor a vagy b" ekvivalense. Ez a Horn-képlet által támasztott korlátozásnak köszönhető.

3. téma.PROLOGUE programozási nyelv

2. sz. előadás

Visual Prolog programozási nyelv

2. Tudás és adatszerkezetek ábrázolása a Prolog programjaiban.

3. Visual Prolog funkció

1. A Visual Prolog nyelv alapvető konstrukciói, programrészek, a Prolog program leíró, eljárási és gépi jelentése

Rövid történelmi háttér:

A 70-es évek elején a Marseille-i Egyetem szakemberei A. Colmeroe vezetésével egy speciális rendszert dolgoztak ki a tételek bizonyítására a Fortran nyelv alapján, amelyet természetes nyelvű állítások feldolgozására használtak. A 80-as évek elején a Dán Műszaki Egyetem Számítástudományi Tanszékén programozói csapat alakult, akik 1982-ben kifejlesztettek egy prológus nyelvi tolmácsot a VAX számára. 1984-ben Leo Jensen, John Goffman, Finn Gronskow fordítóprogramot fejlesztett ki az IBM PC-hez, és megalakult a PDC (Prolog Development Center) cég. A Borland 1990-ig a Turbo Prolog átruházta a jogokat a PDC-re. 1993 óta Viktor Jukhtenko vezetésével a szentpétervári PDC csoport vesz részt a Visual Prolog fejlesztésében. A projektet jelenleg Leo Jensen, Thomas Linder Puls, Viktor Jukhtenko, Jurij Iljin vezeti.

1. A Visual Prolog nyelv alapvető konstrukciói

Nevek

a. A Prologban a visszahívásban a neveket szimbolikus állandók, tartományok, predikátumok és változók jelölésére használják. A név egy betűből (vagy egy aláhúzásból) áll, amelyet nulla vagy több betű, szám vagy aláhúzás bármely kombinációja követ. A névvel kapcsolatban két fontos korlátozás van:

b. A szimbolikus állandó neveknek kisbetűvel kell kezdődniük;

c. A változóneveknek nagybetűvel vagy aláhúzással kell kezdődniük.

Ezeken a korlátozásokon kívül a programban tetszés szerint használhat nagy- és kisbetűket. Például egy nevet olvashatóbbá tehet a nagy és kis betűk keverésével, mint a változóban

LongestVariableName SoFar

vagy aláhúzásjelekkel:

pair_who_might_make_ar_happy_couple (henry_yiii, ann_boleyn)

A Visual Prolog fordító az első betű kivételével nem tesz különbséget a kis- és nagybetűk között. Ez azt jelenti, hogy két változó

ugyanazok.

Kulcsszavak

A következő szavak le vannak foglalva, és nem használhatók felhasználó által definiált névként:

absztrakt elsedef ifndef eljárás

align endclass munkagép védett

mint az enddef tartalmaz predikátumokat

és hibás nyelvi hivatkozás

osztály tények multi single

záradékok hiba nocopy static

konstansok globális nem meghatározott struktúra

adatbázis célobjektum ez

Speciálisan meghatározott predikátumok

A következő predikátumokat a fordító speciálisan kezeli; ezek a nevek nem definiálhatók újra a programban:

chain_insertafter

2. Programrészek

A Visual Prolog program modulokból áll, amelyek mindegyike több szakaszt tartalmaz. Egy programszakaszt egy kulcsszó azonosít, a táblázat szerint. 23.1.

Fordító beállításai A modul elején megadott fordítói beállítások

állandók Nulla vagy több szimbolikus állandó

domainek Nulla vagy több domain hirdetés

tények Nulla vagy több adatbázis-predikátum deklaráció

predikátumok Nulla vagy több predikátum deklaráció

cél Program cél

záradékok Nulla vagy több tagmondat

osztály Nulla vagy több nyilvános (és védett) predikátumok, tények és tartományok deklarációi

a privát predikátumok, tények és tartományok nulla vagy több deklarációjának megvalósítása. Nulla vagy több záradék, amely nyilvános és privát predikátumokat valósít meg (és inicializálja a tényeket)

absztrakt osztály Nulla vagy több nyilvános predikátum és tartomány deklaráció. végrehajtása nem szabad

A program létrehozásához meg kell adni egy célt benne. A program legalább szakaszokat igényel állítmányokÉs záradékok. A legtöbb programnak szüksége van egy szakaszra domainek listák, összetett struktúrák és saját tartományok deklarálásához.

A moduláris programozásban a tartományok, predikátumok és tények kulcsszavakat megelőzheti a global kulcsszóval, jelezve, hogy a későbbi deklarációk globális hatókörrel rendelkeznek, és a deklarált nevek érvényesek minden olyan programmodulban, amely tartalmazza ezeket a globális szakaszdeklarációkat.

Egy program tartalmazhat több tartományt, predikátumot, tény- és záradékszakaszt, valamint több osztály deklarációját és implementációját,

A logikai programban használt tartományobjektumok deklarálására szolgáló szakasz, például:

cím, szerző = szimbólum oldalak = aláírás nélküli

szakasz a logikai programban használt predikátumok deklarálásához, például:

könyv(cím, oldalak) írta_(szerző, cím) hosszú_regény(cím)

A záradékok szakasz, amely meghatározza azokat a tényeket és szabályokat, amelyeket a Prolog a hozzárendelt problémák megoldására használ, például:

cél szakasz, amely megadja a Prolog rendszer feladatát, például:

3. Prolog program leíró, procedurális és gépi jelentése.

A Prolog program általában nem műveletek sorozata, hanem tények halmaza, olyan szabályokkal, amelyek ezek alapján következtetéseket vonnak le. Ezért a Prolog deklaratív nyelvként ismert.

A Prolog programok kétféle kifejezésből állnak: tényekből és szabályokból, amelyeket záradékoknak is neveznek.

· A tények olyan kapcsolatok vagy tulajdonságok, amelyekről ismert, hogy „igaz” értékkel bírnak.

· A szabályok összefüggő kapcsolatok; lehetővé teszik a Prolog számára, hogy logikusan következtessen az egyik információra a másikból. Egy szabály akkor igaz, ha egy adott feltételrendszer igaz.

2. A Prologban minden szabály 2 részből áll: egy fejlécből és egy törzsből, amelyeket egy speciális jel választ el: -.

· A címsor olyan tény, amely igaz lenne, ha több feltétel is igaz lenne. Ezt következtetésnek vagy függő kapcsolatnak nevezik.

· A törzs olyan feltételek halmaza, amelyeknek igaznak kell lenniük a Prolog számára, hogy igazolja, hogy a szabály fejléce igaz.

a tények és a szabályok gyakorlatilag ugyanazok, kivéve, hogy a tényeknek nincs kifejezett testük. A tények úgy viselkednek, mintha testük lenne, ami mindig igaz.

A Prolog mindig az első ténytől és/vagy szabálytól kezdve keresi a megoldást, és végigmegy a tények és/vagy szabályok teljes listáján a végéig.

A Prolog következtetési motorja egy szabályból veszi a feltételeket (a szabály törzsét), és átnézi az ismert tények és szabályok listáját, hogy megpróbálja teljesíteni a feltételeket. Ha minden feltétel igaz, akkor a függő relációt (a szabály fejlécet) igaznak tekintjük. Ha az összes feltétel nem lehet összhangban az ismert tényekkel, akkor a szabály nem ad ki semmit.

A Prolog a Horn-kózusokon alapul, amelyek a predikátumlogikának nevezett formális rendszer részhalmazát képezik. A predikátumlogika a legegyszerűbb módja annak, hogy megmagyarázzuk, hogyan „működik” a gondolkodás, és egyszerűbb, mint a régóta használt aritmetika.

A Prolog a predikátum logikai szintaxis egyszerűsített változatát használja, könnyen érthető és nagyon közel áll a természetes nyelvhez.

A Prolog nyelv nem a nagyszámú aritmetikai művelettel kapcsolatos programozási problémákra szolgál. Ehhez eljárási programozási nyelveket használnak. Azonban minden Prolog rendszer tartalmazza az összes szokásos aritmetikai operátort:

Összeadás - kivonás * szorzás / osztás mod egészek osztásának maradéka div egész osztás

A Prolog tartalmaz egy következtetési mechanizmust, amely mintaillesztésen alapul. A lekérdezésekre adott válaszok kiválasztásával lekéri a tárolt (ismert) információkat. A Prolog megpróbálja tesztelni egy hipotézis igazságtartalmát (más szóval válaszolni egy kérdésre) úgy, hogy olyan információkat kér, amelyekről már tudjuk, hogy igazak.

A Prolog egyik legfontosabb tulajdonsága, hogy amellett, hogy logikusan választ talál az Ön által feltett kérdésekre, képes foglalkozni az alternatívákkal és minden lehetséges megoldást megtalálni. Ahelyett, hogy egyszerűen a program elejétől a végéig dolgozna, a Prolog visszamehet, és több „utat” is megvizsgálhat a probléma minden részének megoldásához.

2. Tudás és adatszerkezetek ábrázolása a Prolog programjaiban.

2. adatstruktúrák a Prolog programokban.

1. A tárgykörrel kapcsolatos ismeretek megjelenítése a Prolog tudásbázis tényei és szabályai formájában.

A fák rekurzív adatstruktúrák

Egy adattípus rekurzív, ha lehetővé teszi a sajátjával azonos struktúrákat tartalmazó struktúrákat.

A legfontosabb rekurzív adattípus a lista, bár úgy tűnik, hogy nem közvetlenül rekurzív konstrukció.

A Visual Prolog lehetővé teszi valóban rekurzív típusok meghatározását, amelyekben a mutatók automatikusan jönnek létre és kezelhetők. Például definiálhat egy fát így:

fatípus = fa(karakterlánc, fatípus, fatípus)

Ez a deklaráció azt mondja, hogy egy fa olyan fafüggvényként van írva, amelynek argumentumai egy karakterlánc és két másik fa.

Belső tény (adat) adatbázis

A belső ténybázis olyan tényekből áll, amelyeket futás közben közvetlenül hozzáadhat és eltávolíthat a Visual Prolog programból. A szakaszban deklarálhatja a belső adatbázist leíró predikátumokat tények program, és alkalmazza ezeket a predikátumokat ugyanúgy, mint a predikátumok részben leírt predikátumokat.

Ha új tényeket szeretne hozzáadni az adatbázishoz, a Visual Prolog assert, asserta, assertz predikátumokat használ, valamint a visszavonási és visszavonási predikátumokat használja a meglévő tények eltávolítására. Megváltoztathatja a tényalap tartalmát úgy, hogy először töröl egy tényt, majd beilleszti a tény új verzióját (vagy egy teljesen más tényt). A consult/1 és a consult/2 predikátumok tényeket olvasnak ki egy fájlból és hozzáadják a belső adatbázishoz, és a mentés/1 és mentés/2 fájlba mentik a belső tényadatbázis tartalmát.

A Visual Prolog az adatbázishoz tartozó tényeket ugyanúgy értelmezi, mint a szabályos predikátumokat. A belső tényalap predikátum tényei egy könnyen módosítható táblázatban vannak tárolva, míg a normál predikátumokat bináris kódba fordítják a maximális sebesség érdekében.

A tények kulcsszó (az elavult szóadatbázis szinonimája) határozza meg a tények szakasz deklarációjának elejét

Külső adatbázisok a Visual Prologban

A Visual Prolog belső adatbázis-rendszere, amely az asserta, assertz, retract és retractall predikátumokat használja, egyszerű és könnyen használható. Nagy adatbázisokkal való munkavégzés esetén azonban gyengébb a sebesség, részben ezen megfontolások miatt jött létre külső adatbázis-rendszer, azzal amellyel például létrehozhat:

Készletkezelő rendszer nagyszámú nyilvántartással;

Szakértői rendszer sok kapcsolattal és kevés, összetett szerkezetű rekorddal;

Számviteli rendszer nagy szövegek adatbázisban való tárolására;

Felhasználói adatbázis, amelyben az adatok nem relációs módon kapcsolódnak egymáshoz.

2. adatstruktúrák a Prolog programokban

Listák és rekurzió

A listák, azaz olyan objektumok feldolgozása, amelyek tetszőleges számú elemet tartalmaznak, a Prolog hatékony funkciója. Ez a fejezet elmagyarázza, mik azok a listák, és hogyan kell deklarálni őket. Ezután számos példát mutat be, amelyek bemutatják, hogyan használható a listafeldolgozás a feladatokban. Ezután definiálunk két jól ismert Prolog predikátumot, a tagot és az appendet, miközben figyelembe vesszük a listafeldolgozás procedurális és rekurzív vonatkozásait.

Ezt követően egy szabványos Visual Prolog predikátum kerül meghatározásra - findall, amely lehetővé teszi az összes megoldás megtalálását és összegyűjtését egyetlen cél érdekében.

Mi az a lista?

A Prológusban lista - ez egy olyan objektum, amely véges számú egyéb objektumot tartalmaz. A listák nagyjából összehasonlíthatók más nyelvű tömbökkel, de a tömbökkel ellentétben a listáknak nem kell előre deklarálniuk a méretüket.

Természetesen vannak más módok is több objektum egyesítésére. Ha az objektumok száma előre ismert, akkor argumentumokat állíthat be egy összetett adatszerkezetbe. Ha az objektumok száma nincs megadva, akkor rekurzív összetett adatstruktúra, például fa is használható. De a listákkal általában könnyebb dolgozni, mert a Visual Prolog világosabb jelölést biztosít számukra.

A lista minden összetevője meghívásra kerül elem. Lista adatstruktúra létrehozásához a listaelemeket vesszővel kell elválasztani és szögletes zárójelek közé kell tenni. Íme néhány példa:

["valerie ann", "Jennifer caitlin", "benjamin thomas"]

Listák nyilatkozata

Ha egy tartományt egész számok listájához szeretne deklarálni, olyan tartománydeklarációt kell használnia, mint például:

integerlist = integer*

A szimbólum (*) jelentése "valaminek a listája"; így az integer* jelentése "egész számok listája".

Vegye figyelembe, hogy a "lista" szónak nincs különösebb jelentése a Visual Prologban. Akár Zanzibárnak is nevezheti a listát. A * jelölés (nem a név) jelzi a fordítónak, hogy ez egy lista.

A listaelemek bármiek lehetnek, beleértve a többi listát is. Azonban minden elemének ugyanahhoz a tartományhoz kell tartoznia. Az elemek tartománydeklarációjának a következő formátumúnak kell lennie:

elemlista = elemek*

Itt az elemek egyetlen típusúak (például: egész szám, valós vagy szimbólum), vagy különálló elemek halmaza, amelyeket különböző függvények jelölnek meg. A Visual Prologban nem keverhet szabványos típusokat egy listában. Például a következő deklaráció helytelenül definiál egy listát, amely egész számokból, valós számokból vagy azonosítókból álló elemekből áll:

Fej és farok

A lista egy rekurzív összetett objektum. Két részből áll - a fejből, amely az első elem, és a farokból, amely egy lista, amely minden további elemet tartalmaz. A lista vége mindig lista, a lista feje mindig egy elem. Például:

fej [a, b, c] az a

farok [a, b, c] van

Mi történik, ha egy szingli listára kerülsz? A válasz

head [s] is with

farok [s] van

Munka a listákkal

A Prolognak van egy módja a fej és a farok kifejezett elválasztására. Ahelyett, hogy az elemeket vesszővel választanánk el, ez megtehető egy függőleges „|” sávval. Például:

[a, b, c] ekvivalens [a| ] és folytatva a folyamatot,

[a | ] egyenértékű az [a | ]], amely az [a| ] ]

Ugyanabban a listában mindkét típusú határolót használhatja, feltéve, hogy a függőleges sáv az utolsó határoló. Ha szükséges, beírhatja az [a, b, c, d] karakterláncot [a, b [c, d] ]-ként.

Listák használata

A lista egy rekurzív összetett adatstruktúra, ezért a feldolgozásához algoritmusokra van szükség. A lista feldolgozásának fő módja az egyes elemek végighaladása és feldolgozása a vég eléréséig.

Ez a fajta algoritmus általában két mondatot igényel. Az első azt mondja meg, hogy mit kell tenni egy normál listával (egy fejre és egy farokra osztható lista), a második pedig azt, hogy mit kell tenni egy üres listával.

Listaelemek számlálása

Ellenőrizd, hogy rajta vagy-e a listán

3. FunkcionalitásVizuális Bevezető

1. Moduláris programozás

2. tárgymechanizmus

3. Eszközök grafikus felület létrehozásához

1. Moduláris programozás

A Visual Prolog másik pozitív tulajdonsága, hogy a rendszer képes együttműködni a modulokra osztott programokkal. A modulokat külön-külön írhatja, összekapcsolhatja és lefordíthatja, majd összekapcsolhatja őket egyetlen végrehajtható program létrehozásához. Ha módosítania kell a programot, akkor csak az egyes modulokat kell szerkesztenie és újrafordítania, és nem a teljes program egészét. Ezt minden bizonnyal értékelni fogja, ha nagy programokat ír. Ezenkívül a moduláris programozásnak van egy másik előnye is - alapértelmezés szerint minden predikátum és tartománynév helyi. Ez azt jelenti, hogy a különböző modulok ugyanazokat a neveket használhatják különböző célokra. A Visual Prolog két koncepciót használ a moduláris programozás vezérlésére: globális bejelentésekÉs projektek.

Globális bejelentések

Alapértelmezés szerint a modulban használt összes név helyi. A Visual Prolog programok modulhatárokon át kommunikálnak a globális predikátumokban és osztályokban meghatározott predikátumok használatával. Az ezekben a globális szakaszokban meghatározott tartományokat globális tartományként vagy szabványos tartományként kell meghatározni.

Az 5.2-es verziótól kezdődően a Visual Prolog globális deklarációkezelést biztosít. Ugyanis:

A fő projektmodulnak (célszekcióval) tartalmaznia kell a projekt összes almoduljában deklarált összes globális tartomány (és globális tényszakasz) deklarációit;

Minden más projektmodul csak az ebben a modulban használt globális tartományok deklarációit tartalmazza;

A globális deklarációk a lokálisak után helyezhetők el;

Ha bármely globális deklaráció megváltozik, csak azokat a modulokat kell újra lefordítani, amelyek tartalmazzák ezt a deklarációt.

Globális tartományok

Egy tartományt globálissá tehet, ha beírja a globális tartományok szakaszba. Minden más tekintetben a globális tartományok hasonlóak a lokálisokhoz.

A Visual Prolog 5.2-es verziója a globális tartományok jobb kezelését biztosítja. Már nem szükséges, hogy minden modul egy adott sorrendben tartalmazza az összes globális tartomány azonos deklarációját (a PDC Prologban és a Visual Prolog 5.2 előtti verzióiban egy speciális chkdoms.exe segédprogramot használtak ennek az azonosságnak az ellenőrzésére). Most két szabályt kell követnie:

Csak a fő projektmodulnak (célszakasszal) kell tartalmaznia a projekt összes almoduljában deklarált összes globális tartomány (és globális tényszakasz) deklarációit;

Minden más projektmodul csak az ebben a modulban használt globális tartományok deklarációit tartalmazza.

Ez a következő alapvető előnyöket nyújtja:

Lehetőség előre lefordított könyvtárak létrehozására és használatára (globális tartományok használatával);

Csökkentett fordítási idő: ha bármely globális deklaráció megváltozik, akkor csak azokat a modulokat kell újrafordítani, amelyek tartalmazzák ezt a deklarációt;

A programok több tartományt is használhatnak, mivel egy modul csak azokat a globális tartományokat tartalmazza, amelyeket az adott modul ténylegesen használ.

A Visual Prolog vizuális fejlesztői környezet rugalmas, automatikus mechanizmust biztosít a globális tartomány deklarációinak projektmodulokba való beépítésének kezelésére. Ennek a mechanizmusnak a lényege a Fájl felvétele a modulhoz párbeszédpanel, amely új modul létrehozásakor aktiválódik. Kisebb projektek esetén egyszerűsített stratégiát használhat annak biztosítására, hogy minden globális tartomány szerepeljen minden projektmodulban. Ehhez szüksége van:

Globális tények szakaszai

A tények szakasz globális lesz, ha a globális kulcsszót a tények kulcsszó elé illesztjük (az elavult adatbázis kulcsszó is használható).

Megjegyzés

A globális tényrészekből (adatbázisokból) származó egyedi tényekre vonatkozó kötelező inicializálási utasítások csak a fő projektmodulban a cél rész után helyezhetők el.

Mivel a Visual Prolog automatikusan generál egy globális tartományt, amely megfelel az egyes globális tényszakaszok (adatbázisok) nevének, a globális tartományok kezelésére vonatkozó felsorolt ​​szabályoknak a globális tényszakaszokra is vonatkozniuk kell.

projektek

Ha VDE-t használ, az Application Expert automatikusan kezeli az új projektek létrehozását és a projektmodulok hozzáadását/eltávolítását. A VDE Make szolgáltatás automatikusan elvégzi a célmodul létrehozásához szükséges fordítási és összekapcsolási műveleteket a projekt forrásmoduljaiból.

Ezért itt csak két olyan funkciót kell elmagyaráznunk, amelyek fontosak lesznek, ha a parancssort használja a fordító meghívására:

Hogyan használja a Visual Prolog projekt a szimbólumtáblázatot.

A Visual Prolog programoknak rendelkezniük kell céllal (tartalmaznak egy Cél részt).

Alapértelmezés szerint a fordító ellenőrzi, hogy a fordítandó fájlban van-e cél szakasz, és ha nincs, akkor hibát generál. A többmodulos projektekben azonban csak egy (fő) projektmodul tartalmaz célszakaszt. A projekt további moduljainak összeállításához a fordítót tájékoztatni kell arról, hogy ezek a modulok a projekt részei, ezért nem tartalmaznak célrészt. Ezt a parancssori fordítói paraméter - g - teszi meg.

Osztályok és objektumok

A Visual Prolog tartalmaz egy objektummotort, amely egyesíti a logikai és az objektum-orientált programozási (OOP) paradigmákat.

Ahhoz, hogy egy rendszert objektumorientáltnak lehessen tekinteni, négy feltételnek kell megfelelnie. Ez:

Egységbezárás;

öröklés;

egyéniség.

Egységbezárás

A tokozás és a modularitás szükségessége jól ismert. A beágyazott objektumok segítenek strukturáltabb és olvashatóbb programok létrehozásában, mivel az objektumok fekete dobozként kezelhetők. Nézz meg egy összetett programot, és keress egy részt, amit deklarálhatsz és le tudsz írni. Kapcsolja be egy objektumba, készítsen interfészt, és folytassa így, amíg a programot alkotó összes részproblémát deklarálják. Miután az egész programot objektumokra bontotta, és megbizonyosodott arról, hogy mindegyik megfelelően működik, elvonatkoztathatja őket.

Az OOP-t adatvezérelt programozásnak is nevezik. Valójában hagyja, hogy az objektumok maguk végezzék el a munkát. Olyan metódusokat tartalmaznak, amelyek meghívódnak az objektumok létrehozásakor, törlésekor és általában az objektumokhoz való hozzáférés során. A metódusok más objektumok metódusait is meghívhatják.

Objektumok és osztályok

Az adatok tárolásának módja a hagyományos programozási nyelvekben általában nehezen érthető és nem alkalmas modellezésre. A tárgyakkal sokkal könnyebb dolgozni, mert közel állnak a valódi dolgok emberi felfogásához, és önmagukban is modellező eszközt jelentenek.

Egy tárgy sokkal összetettebb adatstruktúra, mint egy lista. Elemi szinten az objektum konzisztens adatok deklarációja. Ez a nyilatkozat tartalmazhat olyan predikátumokat, amelyek ezen adatokon működnek. Az OOP terminológiában ezeket a predikátumokat metódusoknak nevezik. Minden osztály az objektumok és műveletek (metódusok) egyedi típusát képviseli, amelyek képesek ilyen objektumok létrehozására, törlésére és manipulálására.

Osztály egy felhasználó által meghatározott objektumtípus; az osztály létrehozhat ilyen típusú objektumokat. A példány egy objektum tényleges állapota. Egy osztálynak annyi példányát (objektumát) határozhatja meg, amennyire szüksége van.

Öröklés

Az OOP egy hatékony modellezési eszköz. Az objektumok az absztrakció legmegfelelőbb szintjén definiálhatók. Ehhez a szinthez képest alacsonyabb szinteken definiálhatók leszármazott objektumok, magasabb szinteken pedig szülőobjektumok. Egy objektum örökölhet adatokat és metódusokat a magasabb szinteken meghatározott objektumoktól. Így az objektumok a legegyszerűbb módja a moduláris programok létrehozásának.

Egyéniség

Minden tárgy egyedi. Az objektumok megváltoztatják állapotukat, és mivel az objektumok állapotai csak tagpredikátumaikon (vagyis az osztálydefiníción belül deklarált predikátumokon) keresztül figyelhetők meg, egy objektum csak önmagával azonos. Így még ha két objektum állapota megegyezik is, az objektumok nem azonosak, mert az egyik objektum állapotát meg tudjuk változtatni anélkül, hogy a másik állapotát megváltoztatnánk, ilyenkor az objektumok nem azonosakká válnak.

Egy tárgy nagyon fontos jellemzője, hogy egyénisége megmarad annak ellenére, hogy tulajdonságai változnak. Egy objektumot mindig a rá való hivatkozáson keresztül érünk el. Sok különböző hivatkozáson keresztül is elérhető, és mivel egy objektum csak önmagával azonos, több hivatkozást is szervezhetünk ugyanarra az objektumra.

GUI-készítő eszközök

A Visual Programming Interface (VPI) egy magas szintű programozási interfész, amely Visual Prolog programok létrehozását segíti elő, és képes támogatni a modern operációs rendszerek grafikus képességeit használó, összetett interfészt. Windows, menük, párbeszédpanelek, vezérlők, tollak, ecsetek, kurzorok, rajzok stb. – ezek az erőforrások és eszközök mindegyike megtalálható a Visual Prologban egyszerű szerkezetként,

A VPI lehetővé teszi hordozható forráskód létrehozását, amely lefordítható 16 bites konfigurációban MS Windows 3.1* alatt, 32 bites Windows 95/98/ME, Windows NT/2000 alatt, OS/2 prezentációkezelő alatt .

Eseményvezérelt alkalmazások

A modern operációs rendszer amellett, hogy egy színvonalas szolgáltatást nyújt, aktívan részt vesz az összes alkalmazás végrehajtásának folyamatában. Az alkalmazás az operációs rendszeren keresztül közvetetten kommunikál a felhasználóval. Amikor a felhasználó tesz valamit (nyomja meg a billentyűt, mozgassa az egeret, kattintson egy ikonra stb.), a hardver interfész eseményt generál, az operációs rendszer lefordítja az eseményt egy szabványos üzenetformátumba, és értesítő üzenetet küld a kiváltó elemnek. azt. Általában ez az aktív ablak.

Az események aszinkron módon érhetnek el egy alkalmazást, ezért az alkalmazást úgy kell megírni, hogy az események sorrendjétől függetlenül biztosítsa az összes adat és folyamat integritását. A grafikus felhasználói felület alapú alkalmazásoknak reagálniuk kell a környezetükre, és nem emulálhatják a DOS-alkalmazások szabványos viselkedését. Az inaktív ablakok mögött lévő alkalmazások aktívak maradhatnak, írhatnak vagy rajzolhatnak inaktív ablakaikban, és mindez látható lesz, kivéve, ha az ablakot bezárja vagy le nem fedi egy másik. Egy alkalmazás bármikor aktiválhatja az egyik ablakát, hogy bevitelt kérjen. Az éppen aktív ablak inaktívvá válik, de alkalmazása tovább fut.

Munka közben általában különféle helyzetek adódhatnak, például a felhasználó sürgős telefonhívást kaphat. Az ablakrendszer gomb megnyomásával megválaszolható Feladat vagy a fájl menüt, és zárja be az alkalmazást. Az operációs rendszer az ablak eseménykezelőjének küldött üzenettel értesíti az alkalmazást az eseményről Feladat. A legtöbb többfeladatos rendszeren az operációs rendszer bármikor szüneteltetheti az alkalmazást. Az alkalmazás soha nem fogja tudni, hogy a felhasználó leállítást kért-e, vagy az operációs rendszer észlelt-e áramkimaradást. Vagy ha egy alkalmazás be akar zárni egy ablakot, akkor egy CloseRequest-et küld az ablaknak, ugyanúgy, mint a felhasználó, amikor kiválasztja a parancsot. Bezárás az ablak menüjében Feladat.

Természetesen a menüpontok és a vezérlőgombok letiltásával csökkenthető a lehetséges események és üzenetek száma. A GUI-alkalmazások üzenetkezelési része nagyon összetett lehet, de ez az ára a nagyobb rugalmasságnak. A Prolog és a VPI nagymértékben leegyszerűsíti a programozó munkáját, amikor az alkalmazást az operációs rendszer grafikus felhasználói felületéhez köti. A Code Expert és a Browser megkönnyíti egy adott eseménynek megfelelő kód megtalálását és feldolgozását, amely egy adott üzenetre válaszol, és ritkán kell megbirkóznia a mögöttes üzenettovábbítási logika bonyolult részleteivel.

Ablak

Az ablak egy téglalap alakú tér a képernyőn. Az ablakot az alkalmazás adatok megjelenítésére és bevitelére használja. A teljes képernyőterületet speciális „képernyőablakként” kezeljük. Minden ablak megosztja a képernyőt más ablakokkal, beleértve a más alkalmazásokból származó ablakokat, és néha az operációs rendszerből származó ablakokat is. Ezek az ablakok átfedhetik egymást.

Nem számít, hány alkalmazás fut egyszerre, mindig csak egy ablak aktív, ami azt jelenti, hogy csak az az ablak fogadhat bemenetet a felhasználótól. Az aktív ablak mindig a többi ablak fölött van.

Minden dinamikus ablaklétrehozó predikátum struktúrát alkot a memóriában, majd ennek a vezérlőszerkezetnek megfelelően jeleníti meg az ablakot, és egy fogópontot rendel az ablakhoz, ami egy egyedi szám. A fogantyúk minden ablakhoz hozzá vannak rendelve, láthatók és rejtettek is. Szinte az összes többi VPI-predikátumhoz egy ablak fogantyúra van szükség bemeneti paraméterként a hozzáféréshez. Mindenféle betöltött és használatra kész erőforrásnak van azonosítója vagy kezelője is, amelyekkel elérhetők.

Egy alkalmazásban minden ablakot vagy párbeszédpanelt nemcsak egy memóriastruktúra képvisel, hanem egy ablakesemény-kezelő predikátum is, amelyre a VPI üzeneteket küld, amikor az ablakhoz kapcsolódó felhasználói vagy rendszeresemények fordulnak elő. Ráadásul ezek az üzenetek nem feltétlenül szinkronizálódnak az alkalmazással. Egy alkalmazás eseményeket is küldhet bármely eseménykezelőjének. A VPI-ben az eseménytartomány eseménynevei mindig e_ előtaggal vannak ellátva.

Ablaktípusok

Az ablakokat típus szerint osztályozzák. Az ablaktípusoknak az ablaktípus tartományhoz kell tartozniuk, és w_ (ablakok), wc_ (vezérlők) vagy wd_ (párbeszédpanelek) előtaggal kell rendelkezniük. Az ablak típusát egy ablakfogóból a win_GetType predikátum meghívásával kaphatjuk meg. A lehetséges ablaktípusok listája.

Normál dokumentumablak

Gyermek ablak

Képernyő ablak Képernyő

Modális párbeszédpanel

Modell nélküli párbeszédpanel

Parancs gomb

Kapcsoló

Vízszintes görgetősáv

Függőleges görgetősáv

Mező szerkesztése

Statikus szöveg

Legördülő lista

Mező szerkesztése legördülő listával

Csoportblokk

Piktogram

Egyéni (felhasználó által meghatározott) vezérlők

Nyomtatás közben használt belső ablak

A képkészítés során használt belső ablak

A metafájl létrehozása során használt belső ablak/

A Prologue megjelenése a logika, a matematika és a programozás fejlődésének volt köszönhető. Ez utóbbi játszotta a legjelentősebb szerepet. A logikai és matematikai szakemberek kísérletet tettek a programozás „helyes útra” állítására, de az információs technológia fejlődése egészen más eredményt mutatott.

A pragmatikus kötelező programozás ígéretesebbnek bizonyult. A „Prolog” sikeres volt programozási nyelvként, de nem vált a mesterséges intelligencia alapjává.

Klasszikus programozás vs logika

Az ember logikusan és ésszerűen hoz nehéz döntéseket. Szinte gondolkodás nélkül az ember bölcsen cselekszik. Ha nem vesszük figyelembe az információgyűjtést, azok elemzését és összetett számításokat igénylő döntéseket, akkor minden eredmény gyors, pontos és ésszerű.

Ez a tény mindig illuzórikus okot adott arra, hogy egy döntési eszköz létrehozását egyszerű dolognak tekintsük. A Prologue megjelenésével úgy tűnt, hogy a mesterséges intelligencia kérdése technológiai kérdés, és a Homo sapiens a robotika három törvényével állt elő. A mesterséges intelligencia azonban szellem maradt, és kiderült, hogy a robotika három törvénye egy tündérmeséből származik - „csináld, nem tudom, mit”.

A szó klasszikus értelmében vett programozás (gyakran használják a „procedurális”, „imperatív” vagy „funkcionális” kifejezéseket) kialakult és sikeresen túljutott a 80-90-es évek „zaklatott időin”, amikor számtalan programozási nyelv létezett.

„Pascal” és „Si” demonstrációs harca sokáig tartott és brutális volt, de semlegesen és csendesen végződött. Marad egy jó programozási nyelv ötlete és annak számos sikeres megvalósítása.

Ez nem jelenti azt, hogy a Prolog mint programozási nyelv ne fejlődött volna. Ám a kitűzött célokat nem érte el. Ma már nemcsak kimondhatjuk, hanem igazolhatjuk is: a „Prológ” egy akadémiai nyelv a következők számára:

  • Tanulási célok;
  • predikátum logika;
  • matematika;
  • szűk alkalmazás.

Kétséges, hogy ezt az állítást meg lehet-e cáfolni. A mesterséges intelligencia nemcsak széles körben alkalmazott, hanem egy nagyon komoly esemény is, amely gyökeresen megváltoztatja a társadalmi szerkezetet és a világképet.

Prolog nyelvű programozás a mesterséges intelligencia számára nem történt meg: a nyelv több mint negyven éves története során egyetlen radikálisan új, a köztudat szempontjából releváns, ennek ellenkezőjét jelző esemény sem történt.

Az objektív valóság a következő: nem annyira a legerősebb marad életben, hanem az, ami keresett és releváns.

A "Prolog" egy deklaratív programozási nyelv

Jó, ha van egy eszköz a tények és szabályok leírására, de mi értelme? A tények és szabályok tökéletesen illeszkednek egy normál adatbázisba. Egy képzett klasszikus programozó interaktív párbeszédet biztosít a felhasználónak, az utóbbi pedig megoldja a problémáit.

Ha szükséges, a programozó finomítja a párbeszédet, a felhasználó pedig kiegészíti a tények és szabályok adatbázisát. Abszolút működő és évtizedek óta tesztelt lehetőség a már megoldott és megoldható problémák tömegének megvalósítására.

A tények és szabályok deklaratív bemutatása a Prolog programozási nyelv bármely megvalósításában konvenció, kísérlet a valóság intellektuális állapotában való formalizálására. A hagyományos programozás nem érinti az intellektust. A klasszikus programozás elégedett a pozícióval: az adatok leírásával és feldolgozásával. Sok probléma van itt, de sok zseniális megoldás működik.

A "Prolog" mint programozási nyelv a tények:

  • anya (Maria, Natasha); - Maria - Natasha anyja;
  • apa (Jevgenyij, Marina); - Evgeniy Marina apja.

Itt egy tény azonnal túlzásba esik: a „Maria” és a „Marina” különböző nevek. Semmi sem akadályozza meg, hogy hozzátegye a tényt:

  • apa (Eugene, Maria); - Evgeniy Maria apja.

Ezek a leírások életre keltik a szabályokat:

  • szülő(x,y)<- папа (x, y);
  • szülő(x,y)<- мама (x, y);

De nem engedik meg azt a következtetést levonni, hogy apa Marina apja, Marina pedig Maria anyja. Ez a probléma megoldható, hozzáadhat még egy szabályt, hozzáadhat még egy tényt. De vajon hány ilyen intézkedést kell végrehajtani egy valós helyzetben?

Valójában a „Prolog” mint programozási nyelv a tények és szabályok deklarálásának példája, de nem arra a logikára, amelyhez a klasszikus programozó tudata hozzászokott. A "Prolog" predikátum logikai nyelvként pozicionálja magát, de programozást csak a nyelv egy adott implementációjának fejlesztőitől származó példákon és mintaleírásokon keresztül lehet megtanulni benne.

A Prológus család

Franciaországot tekintik a Prologue szülőhelyének, és 1973 a születési év. A nyelv iránti érdeklődés időnként megújult, de irigylésre méltó stabilitással alábbhagyott. A nyelv mottója: „A predikátum logika elemi! Ez egy módja annak, hogy elmagyarázzuk, hogyan működik a gondolkodás” - és maradt a mottó.

A Prolog programozási nyelv bármely implementációja szigorúan követte a predikátum logikáját, de mindig magában foglalta az eljárási programozás klasszikus elképzeléseit. Helyesebb azt mondani, hogy "imperatív", mivel ezt a kifejezést több formalitással használják, mint procedurális, funkcionális, objektumorientált vagy egyéb.

Minden programozás az adatokról és azok feldolgozásáról szól. A nyelvi konstrukcióknak a lehető legpontosabban kell leírniuk a megoldandó problémát, ezért a Prolog összes ismert implementációja: Turbo Prolog, Win Prolog, SWI Prolog, GNU Prolog, Visual Prolog és mások a deklaratív konstrukciókon kívül közönséges felszólító kifejezéseket is tartalmaznak.

Úgy gondolják, hogy a Prologue családot akadémiai és kutató szervezetek fejlesztették ki, ezért csak fogalmi értelemben beszélhetünk róla közös nyelvként. Mindazonáltal önmagában az a tény, hogy a „Prolog” fogalma él és fejlődik, megfontolható: ennek a nyelvnek van hatóköre, és egy bizonyos körben igény van rá.

A mesterséges intelligencia alapja

A mesterséges intelligencia iránti érdeklődés soha nem lankadt, csak akkor kezdenek beszélni róla, ha a következő alkalom adódik, de a Prolog-t soha nem hozták jobban a mesterséges intelligenciához, mint egy hétköznapi klasszikus programozási nyelvet.

A 80-as évek végén volt egy igazi, releváns és népszerű szellemi projekt, a „The Inventing Machine”. Valódi kísérlet történt arra, hogy a Prolog segítségével hatalmas gyakorlati tudásbázist (adatokat) formalizáljanak a találmányokról, a fizikai, kémiai és egyéb törvényekről.

Az eredményt nem sikerült elérni, túl sok tényt és szabályt kellett Prolog programozási nyelven megírni, amelyek banális imperatív jellegűek voltak. Eközben számos sikeres szoftverterméket implementáltak párhuzamosan hétköznapi nyelveken.

A 90-es évek elején sikeresen megvalósult egy valódi intellektuális rendszer projektje, amely egy 3 év alatti gyermek viselkedését szimulálta EU számítógépen! A Prolog használatának lehetőségét meg sem fontolták.

Ez az intellektuális rendszer nemcsak „kitalálta”, hogy mi az anya és az apa, és miben különbözik Maria Marinától, hanem különösebb erőfeszítés nélkül, önállóan ugrott az ezekről a kérdésekről megszerzett tudásból a labdákba és a kockáktól való különbségeikig, a színekig. tárgyakat és... (!) az elemi matematikához: az egyszerű számtani műveletek egészen más feladatok megoldásával szerzett ismeretei alapján a képességei között szerepeltek.

Nem mondhatjuk, hogy a klasszikus programozás megelőzi a Prologot a mesterséges intelligencia területének elsajátításában, de valódi eredményeket ad.

Ami az intelligenciát mint feladatot illeti, itt láthatóan nem a nyelvben rejlik a kérdés, hanem a megvalósítás gondolatában. Ha az 1991-es assembler „alapjává válhat” egy intelligens szituációs intelligencia rendszernek, akkor a probléma egyértelműen nem a megvalósítás nyelvében, hanem az ötletben van.

A logikai programozás azon a meggyőződésen alapul, hogy nem az embereket kell megtanítani arra, hogy számítógépes műveletekben gondolkodjanak, hanem hogy a számítógépnek emberi utasításokat kell végrehajtania. Tiszta formájában a logikai programozás feltételezi, hogy nem is adnak utasításokat, és a problémára vonatkozó információkat logikai axiómák formájában fogalmazzák meg. Egy ilyen axiómakészlet a hagyományos program alternatívája. Egy ilyen program akkor hajtható végre, ha egy probléma megfogalmazásra kerül, formalizálva egy bizonyítandó logikai állítás formájában ( célnyilatkozat).

Az elsőrendű predikátumszámítás logikájának a programozási nyelv alapjaként való felhasználásának gondolata a 60-as években merült fel, amikor számos automatikus tételbizonyító rendszer és kérdés-felelet rendszer jött létre. 1965-ben Robinson javasolta a felbontási elvet, amely jelenleg a legtöbb következtetési keresőrendszer mögött áll. A feloldási módszert a GPS (általános problémamegoldó) rendszerben alkalmaztuk. Hazánkban olyan PRIZ rendszert dolgoztak ki, amely egy iskolai geometria tankönyvből bármilyen tételt bizonyíthat.

A PROLOG (programozás a logikában) programozási nyelvet 1972-ben fejlesztette ki és vezette be először a Marseille-i Egyetem munkatársainak egy csoportja, Colmeroe vezetésével. A csoport az egyik nyelvről a másikra történő automatikus fordítás problémáján dolgozott. Ennek a nyelvnek az alapja az elsőrendű predikátumszámítás és a felbontási módszer.

A Prolog programozás során a programozó erőfeszítései arra irányuljanak, hogy a megoldandó probléma tartománytöredékének logikai modelljét a tartományobjektumok, azok tulajdonságai és egymáshoz való viszonya tekintetében írja le, nem pedig a szoftver megvalósításának részleteire. Valójában a Prolog nem annyira programozási nyelv, mint inkább az adatok leírásának nyelve és a feldolgozás logikája. A Prolog program a klasszikus értelemben nem ilyen, mivel nem tartalmaz olyan explicit vezérlőstruktúrákat, mint a feltételes utasítások, ciklusoperátorok stb. A feladatban tárgyalt tartománytöredék modelljét képviseli. A probléma megoldását pedig nem számítógépes terminusokkal írják meg, hanem a megoldandó probléma tárgykörében, a manapság divatos objektum-orientált programozás szellemében.

A Prolog lényege a programozás a célok szempontjából. A programozó a különböző típusú objektumok fogalmai és a köztük lévő kapcsolatok segítségével írja le a probléma feltételét, és megfogalmaz egy kérdést. A PROLOG rendszer a kérdésre úgy ad választ, hogy egy beépített keresési eljárás segítségével automatikusan megtalálja a megoldás számítási sorrendjét. 1981 előtt a logikai programozással foglalkozó kutatók száma körülbelül száz volt világszerte. 1981-ben a PROLOG-ot választották az ötödik generációs számítógépek alapnyelvének, és robbanásszerűen megnőtt a logikai programozással foglalkozó kutatók száma. Az egyik legérdekesebb kutatási téma a logikai programozás és a párhuzamosság kapcsolata.

Hol használják jelenleg a Prolog-ot? Ez az automatikus tételbizonyítás, a szakértői rendszerek felépítése, a heurisztikus gépi játékok (például sakk), az automatikus fordítás egyik nyelvről a másikra területe.

Jelenleg a Prolog nyelvnek elég sok implementációja készült: Wisdom Prolog, SWI Prolog, Turbo Prolog, Visual Prolog, Arity Prolog stb.

Tanfolyamunkon az SWI Prolog-ot fogjuk használni. Az SWI-Prolog-ot 1987 óta fejlesztik. Alkotója és fő fejlesztője Jan Wielemaker. Az SWI név a Sociaal-Wetenschappelijke Informaticából származik, amely az Amszterdami Egyetemen működő csoport eredeti neve, ahol Vielemaeker dolgozik.

Az SWI-Prolog lehetővé teszi bármilyen alkalmazás fejlesztését, beleértve a webes alkalmazásokat és a párhuzamos számítástechnikát is, de a fő felhasználási terület szakértői rendszerek, természetes nyelvi feldolgozó programok, oktatási programok, intellektuális játékok stb. Ez egy tolmács. Az SWI Prologban írt programokat tartalmazó fájlok pl.

Az SWI-Prolog-Editor egy programozási környezet az SWI-Prolog nyelvhez, amely tartalmaz egy programszerkesztőt szintaktikai kiemeléssel, egy értelmezőt és egy programhibakeresőt. A környezet fő célja a logikai programozás oktatása Prologban.

Először telepítjük az SWI Prolog-ot, majd az SWI Prolog Editor-t. Az SWI Prolog Editor elindításához futtassa a SwiplEdit.exe fájlt. Az értelmező működésének konfigurálásához egy speciális szerkesztőablakban be kell állítani az értelmező elérési útját a parancs végrehajtásával a szerkesztőben Window-Configuration A Programok lapon állítsa be az értelmező elérési útját a Prolog Folder sorban. Ott a Beállítások lapon a Codepage mezőt cp1251-re kell állítani. A kódlap beállítása szükséges az orosz ábécével beírt karakterlánc-konstansok helyes összehasonlításához az SWI-Prolog-Editor környezetben lévő programszöveg és az SWI-Prolog nyelv között. A program szerkesztőpanelről történő futtatásához mentse el, és nyomja meg az F9 funkcióbillentyűt vagy a megfelelő ikont az eszköztáron. Ha a letöltés sikeres, a Lekérdező panelen a következő jelenik meg:

Konzultál(<имя файла>).

A fájlt a következő paranccsal is letöltheti: [<имя файла>].

Bármilyen módosítás után a programot újra kell tölteni a memóriába. A Prolog interpreter újraindítása a Ctrl+F9 vagy az eszköztár megfelelő ikonjának megnyomásával történik.

A Prolog interpreterből való kilépéshez használja a következő parancsot: halt.

Tények és szabályok

Mint már említettük, a Prolog elsőrendű predikátumszámítást használ. A predikátumok az objektumok közötti kapcsolatokat határozzák meg. Tekintsük a kapcsolódó kapcsolatok fáját:

10. ábra – Kapcsolatfa

1. példa: Ez a fa a következő Prolog programmal írható le.

szülő (mem, bob).

szülő(tom, bab).

szülő(tom, liz).

szülő (bob, anne).

szülő (bob, pat).

szülő (pat, jim).

Szülőkapcsolatunk van két objektum között. A konkrét objektumok közötti kapcsolatok 6 tényét ismertetjük. Az objektumok nevei kis betűkkel kezdődnek (ezek állandók). A Prolog konvenciója az, hogy a konstansok kisbetűvel, a változók pedig nagybetűvel kezdődnek. Miután beírt egy ilyen Prolog programot a szerkesztőbe, betöltheti a programot a Prologba, és kérdéseket tehet fel a szülői kapcsolattal kapcsolatban.

A program kérését a meghívó után kell begépelni - és ponttal kell végződnie. A begépelt kérés végrehajtásához meg kell nyomnia az Enter billentyűt. A választ a kérés alatt adjuk meg. A kérés több sorba is beírható – az Enter billentyűvel lehet új sorba lépni. Ha a sor ponttal végződik, és az Enter billentyűt lenyomják, az SWI-Prolog megkezdi a kérés végrehajtását. Ha egy kérés megválaszolására több lehetőség is lehetséges, akkor az Enter billentyűt használja a következő mindegyik megválaszolásához. Az SWI-Prolog pontosvesszővel választja el a válaszlehetőségeket. A program végrehajtását (alternatív válaszok előállítását) az „a” gomb megnyomásával állíthatja le.

A kérdések lehetnek egyszerűek vagy összetettek (összetett kérdés megírásakor a vesszőt „és” összekötőként használjuk). A prolog válaszokat közvetlenül a kérdés után nyomtatjuk ki. A következő válaszlehetőségek lehetnek:

  • Nem (nem-nek felel meg, vagy a kérdésben szereplő változók értékei nem találhatók);

    Felsorolja a kérdésben szereplő változók lehetséges értékeit. Ebben az esetben az „a” billentyű megnyomásakor a megoldások keresése leáll, az Enter billentyű lenyomására pedig az új megoldások keresése addig folytatódik, amíg az összeset meg nem találja. Az alternatív megoldásokat pontosvessző választja el. A kimenet Igennel végződik, ha nem talált minden megoldást, és Nem-re, ha nincs más megoldás.

A szülők hozzáállásával kapcsolatos kérdés

Kérdés Prolog rendszerben

Prolog rendszer válasz

Bob Pat szülője?

szülő(bob,pat).

Pat Liz gyermeke?

szülő(liz,pat).

Kik Liz szülei?

szülő(X,liz).

Kik Anne gyermekei?

szülő (anne, X).

Kik Bob gyermekei?

szülő(bab, X).

Liznek vannak gyerekei?

szülő(liz,_).

Ki kinek a szülője?

szülő(X,Y).

Kik Tom unokái?

szülő(kötet,X),

szülő(X,Y).

Kik Jim szüleinek a szülei?

szülő (X, Jim),

szülő(Y,X).

Tehát a legegyszerűbb esetben egy Prolog program tényeket és szabályokat ír le.

Tény- ez egy feltétlen állítás (mindig igaz), amely valamilyen szempontból jellemzi az objektumot, vagy kapcsolatot létesít több objektum között. Egy tényhez nincs szükség bizonyításra. A tény így néz ki:

<имя предиката>(O 1,O 2,…,O n).

Figyeljünk arra, hogy a tény végén van egy időszak.<имя предиката>kisbetűvel kell kezdődnie, és tartalmazhat betűket, számokat és aláhúzásjeleket. O i (i = 1,..,n) - a predikátum argumentumai lehetnek konkrét objektumok (konstansok) vagy absztrakt objektumok (változók). Ha bizonyos objektumok betűvel kezdődnek, akkor ennek a betűnek kisbetűnek kell lennie.

A változók nagybetűvel vagy aláhúzással kezdődnek. A Prolog változója, az algoritmikus programozási nyelvekkel ellentétben, egy objektumot jelöl, nem pedig egy memóriaterületet. A Prolog nem támogatja a destruktív hozzárendelési mechanizmust, amely lehetővé teszi egy inicializált változó értékének megváltoztatását, például a kötelező nyelveket. A változók lehetnek szabadok vagy kötöttek. A szabad változó olyan változó, amely még nem kapott értéket. Nem nulla és nem szóköz; egyáltalán nincs értelme. Az ilyen változókat példányosítottnak is nevezik. Azt a változót, amely valamilyen értéket kapott és egy adott objektumhoz van társítva, kötöttnek nevezzük. Ha egy változót valamilyen értékkel példányosítottak, és valamilyen objektum társítva van hozzá, akkor ez a változó már nem módosítható.

A Prolog változó hatóköre egy záradék. A különböző mondatok ugyanazt a változónevet használhatják különböző objektumokra való hivatkozáshoz. A hatályos szabály alól kivételt képez egy névtelen változó, amelyet aláhúzás jelöl. Az anonim változó arra utasítja az értelmezőt, hogy figyelmen kívül hagyja a változó értékét. Ha egy szabályban több névtelen változó van, akkor mindegyik különbözik egymástól, annak ellenére, hogy ugyanazt a szimbólumot használják.

Szabály– bizonyos feltételek teljesülése esetén igaz állítás. A szabály egy feltételes részből (test) és egy kimeneti részből (fej) áll. Fejszabályok egy predikátum, amelynek igazságát meg kell állapítani. Szabálytörzs egy vagy több logikai konnektívumokkal összekapcsolt predikátumból áll: konjunkció (vesszővel jelölve), diszjunkció (pontosvesszővel jelölve) és tagadás (nem vagy \+ jelöli). A szabály így néz ki:

<голова правила > :–­­­­ <тело правила>.

A szabály végén van egy pont is. Felfoghatjuk úgy, hogy a tény olyan szabály, amelynek üres teste van.

Szabályok használhatók az új kapcsolatok leírására.

Példa: Legyen egy kéthelyi kapcsolatú szülők és egy egyhelyi kapcsolatú férfi. Ezeket a kapcsolatokat tények formájában írják le. Leírjuk az új nagyapa bináris relációt a szabályok segítségével: X az Y nagyapja, ha van lánc: X a Z szülője, Z az Y szülője, és X-nek férfinak kell lennie.

nagyapa(X,Y):–szülő(X,Z),szülő(Z,Y),férfi(X).

Példa: Legyen egy bináris relációszülő, tények formájában leírva. Leírjuk az új bináris reláció ősét a szabályok segítségével. X Y őse, ha X Y szülője, vagy X és Y között van egy emberlánc, amelyet a szülői kapcsolat köt össze.

ős(X,Y):–szülő(X,Y).

ős(X,Y):–szülő(X,Z),ős(Z,Y).

Ezeket a szabályokat másképp is fel lehet írni:

ős(X,Y):–szülő(X,Y);

szülő(X,Z),ős(Z,Y).

Ebben a példában az őskapcsolat rekurzív definícióját kaptuk.

Példa. Határozzuk meg a távoli_relatív bináris relációt egy szabály segítségével, amely a meglévő reláció ősét használja. X távoli rokona Y-nek, ha rokonok az ősök, de nem a szülő.

távoli_relatív(X,Y):–ős(X,Y),nem(szülő(X,Y));

ős(Y,X),nem(szülő(Y,X)).

A szabály jobb oldalán @ jeleket használhat az összehasonlításhoz<, @=<, @>=, @> a sorrend teszteléséhez, == az egyenlőség teszteléséhez és \== az egyenlőtlenség teszteléséhez.