1 / 25

Prolog – seznamy

Prolog – seznamy. Jan Hric, 1997 – 2010b KTIML MFF UK URL: http://kti.ms.mff.cuni.cz/~hric/. Tok dat. není rozlišován vstupní vs. výst. argument + vstupní argument - výstupní argument +- arg. obsahuje volné proměnné ? cokoli mod predikátu a volání

Download Presentation

Prolog – seznamy

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Prolog – seznamy Jan Hric, 1997 – 2010b KTIML MFF UK URL: http://kti.ms.mff.cuni.cz/~hric/

  2. Tok dat • není rozlišován vstupní vs. výst. argument • + vstupní argument • - výstupní argument • +- arg. obsahuje volné proměnné • ? cokoli • mod predikátu a volání • př: faktorial(+,?), rodic(-,-) • - systém to nekontroluje, konvence programátora

  3. Tok dat - příklad • definice rodic/2 • predek(X,X). % 1. varianta • predek(X,Y):- rodic(X,Z), predek(Z,Y). • predek(X,X). % 2. varianta • predek(X,Y):- rodic(Z,Y), predek(X,Z). • volání p(+,+), p(+,-), p(-,+), p(-,-) • stejné 1.lepší 2.lepší • ?- p(adam,X) vs. ?- p(X,cyril) • vhodnost závisí na zamýšleném (příp. obvyklém) použití • jiné varianty (přehození cílů, klauzulí) jsou horší

  4. Operace na seznamech • zjistit, zda X je prvkem seznamu L • % member(?X,+L) • member(X,[X|_]). % X je hlavou • member(X,[_|L]):- member(X,L). • % X je v těle • ?- member(b,[a,b,c]). % m(+,+) • yes • ?- member(X,[a,b]). % m(-,+) • X=a; % generovani • X=b; • no

  5. Zpracování rekurzívních dat. struktur • - rekurzívní d.s. se zpracovávají rek. programy • - koncové případy jsou ošetřeny fakty • anebo nerekurzívní klauzulí • - aspoň jeden fakt anebo nerek. klauzule ! • - každý možný tvar d.s. (konstruktor) má být ošetřen - jinak neúspěch • - např. seznamy: []/0, ./2 • stromy: void/0, t/3 • - u member/2 ošetření [] není nutné (ekv. fail) • - není to obvyklé

  6. Member/2 - pokrač. • - příklad podspecifikovaného cíle • ?- member(a,L). % m(+,-) • L = [a|_1]; • L = [_1,a|_2]; • L = [_1,_2,a|_3]; • ... atd. • - vydává se “nekonečně” mnoho řešení backtrackingem • - _číslo je výpis volné proměnné

  7. member – pro asociativní paměť • Položky seznamu: klíč-hodnota % -(k,h) • ?- Tab= [k1-h1,k2-h2,k2-h2b], % generovat • member(k2-HN,Tab). % ke kN najde HN • HN = h2; • HN = h2b; • No - Zde využíváme backtracking, ale lze vracet i všechny hodnoty najednou, v seznamu % lookup(Klic, Pamet, Hodnota) : interface pro vyhledávání v nějaké datové struktuře, nejen seznamu (BVS) lookup(K, Tab, H):- member(K-H, Tab). DC: lookup2D/3: vyhledávání pro dvousložkový klíč, v rovině

  8. První a poslední prvek seznamu • % first(?X,+L) :- X je první v L • a) first(X,[X|_]). % dobrý styl • b) first(X,L):- L=[X|_]. • % last(?X,+L) :- X je poslední v L • last(X,[X]). • last(X,[_|L]):- last(X,L). • %nesymetrický přístup: O(1) na první prvek vs. O(n) na poslední

  9. Spojení seznamů - append(L1,L2,L3) spojí L1 a L2 do L3 append([],L,L). append([X|L1],L2,[X|L3]):- append(L1,L2,L3). ?- append([a,b],[c,d],[a,b,c,d]). %(+,+,+) yes ?- append([a,b],[c,d],L). % mod (+,+,-) L=[a,b,c,d]; no - programátorské idiomy: strukturální rekurze podle prvního arg., tvorba výsledku - skládáním substitucí • Pozn.: polymorfizmus – analyzuje a využívá se pouze „horní“ struktura

  10. ?- X=[a,b], Y=[c,d], append(X,Y,Z). • Z=[a,b,c,d] • tvorba Z - skládáním substitucí • Z = _L3´ • Z = [a|_L3´´] • Z = [a,b|_L3´´´] • Z = [a,b,c,d] X Z Y . . \ . / \ . / \ . . a / \ c / \ [] [] b d

  11. append/3 jako (nederministický) generátor • ?- append(L1,L2,[a,b,c]). % (-,-,+) • L1=[] • L2=[a,b,c]; • L1=[a] • L2=[b,c]; • L1=[a,b] • L2=[c]; • L1=[a,b,c] • L2=[]; • no

  12. concat/2 – Spojení seznamů - převod (řádkové) matice na vektor % concat(+L,-K) – seznam seznamů L spojí do jednoúrovňového seznamu K concat([], []). concat([Xs|L1],L0) :- append(Xs,L2,L0), concat(L1,L2). ?- concat([[1,2],[5],[],[3,4]],Lout). Lout=[1,2,5,3,4] • Počas výpočtu je L0 seznam s volným koncem • L0=[1,2|L2’] ~> [1,2,5|L2’’] ~> [1,2,5,3,4|L2’’’] ~> [1,2,5,3,4] • Nelze použít concat(-,+) pro rozdělení; generuje Xs=[]

  13. Obracení seznamu • % reverse(L,R):- R je L pozpátku • % mod (+,-) • reverse([],[]). • reverse([X|L],R):- reverse(L,R1), • append(R1,[X],R). • - nepříjemnost: složitost O(n^2) • - typická struktura rekurzívních klauzulí • pred :- předvýpočet, % analýza d.s. • pred, % rek. výpočet mezivýsl. • postvýpočet. % použití -”-

  14. Obracení seznamu II - lineárně • reverse(L,R):- rev1(L,[],R). • rev1([],R,R). % mod (+,+,-) • rev1([X|L],A,R):- rev1(L,[X|A],R). • - programátorská technika - akumulátor: “A” • - inicializace - samostatná klauzule/predikát • - odstínění uživatele od technických detailů • - předávání (volné) výstupní prom. v rek.: “R” • - obvykle samostatný argument • - ukončení - předání: “R:=A” %3.arg := 2.arg • x skládání substitucí - přístup na akum. počas výpočtu

  15. Vypouštění prvku % delete(X,I,O):- vypuštěním X ze seznamu I je seznam O; X musí být v I, vypouštíme jeden (lib.) výskyt % mody vypouštění (?,+,-), vkládání (?,-,+); presneji (?,[?],-) • delete(X,[X|I],I). %1 X musí být v arg2 • delete(X,[Y|I],[Y|O]):- %2 • delete(X,I,O). • ?- delete(X,[1,2,3],O). % 3 výsledky • ?- delete(a,I,[b,c]). %vkládání,3x • Varianta: delete1/3: vypustí prvek a vždy uspěje • delete1(X,[],[]). %3+^1+^2: falešné matche! • Nekorektní další výsledky; správnost všech v.! • DC: deleteAll(X,Lin,Lout);

  16. Permutace • % perm(I,O):- O je permutací I, (+,-) • perm([],[]). • perm(I,[X|O]):- delete(X,I,I1), • perm(I1,O). • ?- perm([1,2,3,4],O). • - skládání substitucí • - rekurze podle výstupu • - nederminizmus: řešení se vrací postupně • - jiná (těžší) možnost: vrátit najednou seznam všech permutací • - DC: permutace rekurzí podle vstupu. V jakém pořadí budou vydávány?

  17. Použití append/3 • last(X,L):- append(_,[X],L). • - stejná asymptotická složitost • - horší konkrétní složitost, ošetřujeme v append 1.arg. zbytečně • delete(X,I,O):- append(L1,[X|L2],I), • append(L1, L2, O). • - L1 se prochází 2x • - nejde použít v modu (?,-,+) • - muselo by se napsat jinak: prohodit cíle v těle • prefix/2, suffix/2 - zdarma (bez rekurze) • prefix(P,L):- append(P,_,L). • Pozn. SwIng: budování konkrétních predikátů zvrchu vs. obecné predikáty zespodu (do knihoven) a příp. interface. • Při (častých) změnách méně výkonného kódu ~> méně úprav a chyb

  18. Seznam výsledků % suffix(+L,-S) – S je přípona L, backtrackingem suffix(S,S). % zahrnuje suffix([],[]). suffix([_|L],S):- suffix(L,S). ?- suffix([1,2,3],L). L=[1,2,3] ; L=[2,3] ; L=[3] ; L=[] ; no % suffixy(+L,-Ss) – Ss jsou všechny prípony L v seznamu suffixy([],[[]]). suffixy([X|Xs],[[X|Xs]|Ss]):- suffixy(Xs,Ss). ?- suffixy([1,2,3],Ss). Ss=[[1,2,3],[2,3],[3],[]] % typicky vhodnější

  19. Prefixy % prefixy(+Xs,-Ps)- Ps je seznam všech předpon Xs prefixy([],[[]]). prefixy([X|Xs],[[]|P0]) :- prefixy(Xs,P1), map_pridejH(X,P1,P0). % (1) prefixy v P1 upravíme % přidáva hlavu ke každému seznamu v P1 map_pridejH(_X,[],[]). % jednoúčelové map map_pridejH( X,[P|Ps],[[X|P]|Ps0]):- map_pridejH(X,Ps,Ps0). ?- prefixy([1,2,3],Ps). Ps=[[],[1],[1,2],[1,2,3]] - Průběžně: Ps’=[[],[2],[2,3]], Ps’’=[[],[3]], Ps’’’=[[]] - (1) obecné map: ..,map(pridejH(X),P1,P0),… • pridejH(X,L,[X|L]). % mimo logiku 1. řádu

  20. Seznam prvků ve stromu • tree2list(Strom,Seznam) :- do Seznamu pozbírá prvky Stromu (zleva doprava) • tree2list(void,[]). • tree2list(t(L,X,R),O):- • tree2list(L,OL), %rekurze doleva • tree2list(R,OR), %rekurze doprava • append(OL,[X|OR],O). % spojení inorder - seznam jako výsledek • pre-,post-,inorder (zleva): rozdíl toku programu a “toku” dat • append/3 na stejném místě, ale s jinak předávanými parametry - pro generování stromů (mod (-,+) ) nutno prohodit cíle - pro repr. stromů použít funkční symbol t/3 , a void/0 - nevhodný je (zde trojprvkový) seznam, pokud je počet položek pevný ?- TestData = t( t(void,1,void), 2, t(void,3,void)), tree2list(TestData,V).

  21. Výroba vyváženého stromu - z (uspořádaného) seznamu L chci vyvážený strom T % vyvazBS(+L,-T). vyvazBS([],void). %vyvazBS([X],t(void,X,void)).%konc.podm.není nutná vyvazBS(L,t(TL,X,TR)):- rozdel(L,L1,[X|L2]), % rozdělí na poloviny % pro uspořádaný seznam L: X je medián vyvazBS(L1,TL), vyvazBS(L2,TR). - vyvazBS/2 lze použít v heapsortu pro vybudování správné struktury „haldy“ (bez uspořádání)

  22. Rozdel/3 • rozdel(L,L1,L2) – rozdělí L na první a druhou polovinu, |L1|<=|L2|<=|L1|+1 • Použije v rozdel1/4 druhý arg. jako čítač rozdel(L,L1,L2):- rozdel1(L,L,L1,L2). % interface rozdel1(L2,[],[],L2). % sudá délka rozdel1(L2,[_],[],L2). % lichá délka rozdel1([X|L],[_,_|Acc],[X|L1],L2):- rozdel1(L,Acc,L1,L2).

  23. Množinové výrazy • Výraz obsahuje +/2 pro sjednocení, */2 pro průnik, -/2 pro rozdíl, seznam pro množinu a i(D,H) pro celočíselný interval. - evalS: interpret množinových výrazů, term je program porovnejte s Aho-Corasick: vzorky (1) jsou program (v neplnohodnotném jazyce, doménově specifický jazyk - DSL), který dokonce dokážeme přeložit ad(1): vzorky jsou seznam seznamů (reprezentací) znaků, např. [[h,e],[s,h,e],[h,e,r]] ?- evalS(-(+([3,1,3],i(2,4)),[2,3]), S). S = [1,4] % synt. cukr: zápis s operátory ?-evalS(([3,1,3]+ i(2,4))–[2,3], S). evalS(L,S):- isList(L), list2set(L,S). % převod na (usp.) množinu evalS(i(D,H),S) :- genInterval(D,H,S). evalS(+(E1,E2),S):-evalS(E1,S1), evalS(E2,S2), union(S1,S2,S). evalS(-(E1,E2),S):-evalS(E1,S1), evalS(E2,S2),rozdilS(S1,S2,S). evalS(*(E1,E2),S):-evalS(E1,S1), evalS(E2,S2), prunik(S1,S2,S). • lze použít pro uspořádané i neuspořádané množiny na výst. • Impl. se liší ve výkonných pred. union/3, …, list2set/2, genInterval/2. • DC: interpret multimnožinových výrazů

  24. Shrnutí - tok dat, mody - dvojí použití predikátů: výpočet, test - problémy s podspecifikovanými argumenty v cíli - nedeterminizmus - zpracování rek. struktur - strukturální rekurze - tvorba výsledků - skládáním substitucí - akumulátor • použít interface:pokud rekurzi nejde použít přímo (např. je potřebný „lokální“ argument) - dobrý styl: nezatěžovat uživatele „technickými“ zbytečnostmi - seznam výsledků, místo vracení backtrackingem - o rekurzi víte vše, dále vestavěné predikáty, programátorské techniky a příklady

  25. Autotest • naprogramujte permutace pomocí vkládání prvku a akumulátoru • návod: použijte akumulátor na permutaci dosud zpracovaného prefixu vstupního seznamu • napište rozdělení prvků seznamu na sudé a liché (podle pořadí) • - v jednom průchodu seznamem • - správně ošetřete seznam liché délky • - použití v “rozděl a panuj”: mergesort • ? je append/3 použitelný v modu (+,-,-) • ? jak se bude chovat volání: append([a,b],X,Y) • - pokud má otázka smysl

More Related