Seznamy (pokračování) Cvičení: append/2 appendC [], S, S ). % (1) append( [X|S1], S2, [X|S3] ) :- append( S1, S2, S3). % (2) :- append([1,2],[3,4],A). Hana Rudová, Logické programování I, 14. března 2011 2 Seznamy Cvičení: append/2 appendC [], S, S ). % (1) append( [X|S1], S2, [X|S3] ) :- append( S1, S2, S3). % (2) :- append([1,2],[3,4],A). I (2) I A=[1|B] Hana Rudová, Logické programování I, 14. března 2011 2 Seznamy Cvičení: append/2 appendC [], S, S ). % (1) append( [X|S1], S2, [X|S3] ) :- append( S1, S2, S3). % (2) :- append([1,2],[3,4],A). I (2) I A=[1|B] I :- append([2],[3,4],B). Hana Rudová, Logické programování I, 14. března 2011 2 Seznamy Cvičení: append/2 appendC [], S, S ). % (1) append( [X|S1], S2, [X|S3] ) :- append( S1, S2, S3). % (2) :- append([1,2],[3,4],A). I (2) I A=[1|B] I :- append([2],[3,4],B). I (2) | B=[2|C] => A=[1,2|C] Hana Rudová, Logické programování I, 14. března 2011 2 Seznamy Cvičení: append/2 appendC [], S, S ). % (1) append( [X|S1], S2, [X|S3] ) :- append( S1, S2, S3). % (2) :- append([1,2],[3,4],A). I (2) I A=[1|B] I :- append([2],[3,4],B). I (2) | B=[2|C] => A=[1,2|C] I :- append([],[3,4],C). Hana Rudová, Logické programování I, 14. března 2011 2 Seznamy Cvičení: append/2 appendC [], S, S ). % (1) appendC [X|S1], S2, [X|S3] ) :- append( S1, S2, S3). % (2) :- append([1,2],[3,4],A). I (2) I A=[1|B] I :- append([2],[3,4],B). I (2) | B=[2|C] => A=[1,2|C] I :- append([],[3,4],C). I (1) | C=[3,4] => A=[1,2,3,4], I yes Hana Rudová, Logické programování I, 14. března 2011 2 Seznamy Optimalizace posledního volání Last Call Optimization (LCO) Implementační technika snižující nároky na paměť Mnoho vnorených rekurzivních volání je náročné na paměť Použití LCO umožnuje vnorenou rekurzi s konstantními palmetovými nároky Typický príklad, kdy je možné použití LCO: A procedura musí mít pouze jedno rekurzivní volání: v posledním cíli poslední klauzule A cíle predcházející tomuto rekurzivnímu volání musí být deterministické -i> p( ... ) :- ... % žádné rekurzivní volání v těle klauzule p( ...):- ... % žádné rekurzivní volání v těle klauzule p(...) :- !, p( ... ). % rez zajišťuje determinismus & Tento typ rekurze lze převést na iteraci Hana Rudová, Logické programování I, 14. března 2011 3 Seznamy LCO a akumulátor -í* Reformulace rekurzivní procedury, aby umožnila LCO VýpoCet délky seznamu length( Seznam, Delka ) length( [], 0 ). length( [ H | T ], Delka ) length( T, Delka0 ), Delka is 1 + Delka0. Hana Rudová, Logické programování I, 14. března 2011 4 Seznamy LCO a akumulátor -í* Reformulace rekurzivní procedury, aby umožnila LCO & VýpoCet délky seznamu 1ength( Seznam, Delka ) 1ength( [], 0 ). 1ength( [ H | T ], Delka ) :- 1ength( T, DelkaO ), Delka is 1 + DelkaO. -í* Upravená procedura, tak aby umožnila LCO: % 1ength( Seznam, ZapocitanaDelka, CelkovaDelka ): % CelkovaDelka = ZapocitanaDelka + ,,poCet prvků v Seznam'' Hana Rudová, Logické programování I, 14. brezna 2011 4 Seznamy LCO a akumulátor -í* Reformulace rekurzivní procedury, aby umožnila LCO & VýpoCet délky seznamu 1ength( Seznam, Delka ) 1ength( [], 0 ). 1ength( [ H | T ], Delka ) :- 1ength( T, DelkaO ), Delka is 1 + DelkaO. -í* Upravená procedura, tak aby umožnila LCO: % 1ength( Seznam, ZapocitanaDelka, CelkovaDelka ): % CelkovaDelka = ZapocitanaDelka + ,,poCet prvků v Seznam'' 1ength( Seznam, Delka ) :- 1ength( Seznam, 0, Delka ). % pomocný predikát 1ength( [], Delka, Delka ). % celková délka = zapocítaná délka 1ength( [ H | T ], A, Delka ) :- A0 is A + 1, 1ength( T, A0, Delka ). & Prídavný argument se nazývá akumulátor Hana Rudová, Logické programování I, 14. brezna 2011 4 Seznamy max_list s akumulátorem Výpočet nejvetšího prvku v seznamu max_list(Seznam, Max) max_list([X], X). max_list([X|T], Max) max_list(T,MaxT), ( MaxT >= X, !, Max = MaxT Max = X ). Hana Rudová, Logické programování I, 14. března 2011 5 Seznamy max_list s akumulátorem Výpočet nejvetšího prvku v seznamu max_list(Seznam, Max) max_list([X], X). max_list([X|T], Max) :-max_list(T,MaxT), ( MaxT >= X, !, Max = MaxT Max = X ). max_list([H|T],Max) :- max_list(T,H,Max). max_list([], Max, Max). max_list([H|T], CastecnyMax, Max) :-( H > CastecnyMax, !, max_list(T, H, Max ) max_list(T, CastecnyMax, Max) ). Hana Rudová, Logické programování I, 14. března 2011 5 Seznamy Akumulátor jako seznam Nalezení seznamu, ve kterém jsou prvky v opaCném pořadí reverse( Seznam, OpacnySeznam ) -i- reverse( [], [] ). reverse( [ H | T ], Opacny ) :- Hana Rudová, Logické programování I, 14. brezna 2011 6 Seznamy Akumulátor jako seznam Nalezení seznamu, ve kterém jsou prvky v opaCném pořadí reverse( Seznam, OpacnySeznam ) -i- reverse( [], [] ). reverse( [ H | T ], Opacny ) :-reverse( T, OpacnyT ), append( OpacnyT, [ H ], Opacny ). & naivní reverse s kvadratickou složitosti Hana Rudová, Logické programování I, 14. brezna 2011 6 Seznamy Akumulátor jako seznam Nalezení seznamu, ve kterém jsou prvky v opaCném poradí reverse( Seznam, OpacnySeznam ) -i- reverse( [], [] ). reverse( [ H | T ], Opacny ) :-reverse( T, OpacnyT ), append( OpacnyT, [ H ], Opacny ). & naivní reverse s kvadratickou složitosti reverse pomocí akumulátoru s lineární složitostí -i- % reverse( Seznam, Akumulator, Opacny ): % Opacny obdržíme prídáním prvku ze Seznam do Akumulator v opacnem poradi Hana Rudová, Logické programování I, 14. brezna 2011 6 Seznamy Akumulátor jako seznam Nalezení seznamu, ve kterém jsou prvky v opacném poradí reverse( Seznam, OpacnySeznam ) -i- reverse( [], [] ). reverse( [ H | T ], Opacny ) :-reverse( T, OpacnyT ), append( OpacnyT, [ H ], Opacny ). & naivní reverse s kvadratickou složitosti reverse pomocí akumulátoru s lineární složitostí -i- % reverse( Seznam, Akumulator, Opacny ): % Opacny obdržíme prídáním prvků ze Seznam do Akumulator v opacnem poradi reverse( Seznam, OpacnySeznam ) :- reverse( Seznam, [], OpacnySeznam). reverse( [], S, S ). reverse( [ H | T ], A, Opacny ) :- reverse( T, [ H | A ], Opacny ). % pridání H do akumulátoru Hana Rudová, Logické programování I, 14. b rezna 2011 6 Seznamy Akumulátor jako seznam JS> Nalezení seznamu, ve kterém jsou prvky v opaCném pořadí reverse( Seznam, OpacnySeznam ) -i- reverse( [], [] ). reverse( [ H | T ], Opacny ) :-reverse( T, OpacnyT ), append( OpacnyT, [ H ], Opacny ). & naivní reverse s kvadratickou složitosti -i* reverse pomocí akumulátoru s lineární složitostí -i- % reverse( Seznam, Akumulator, Opacny ): % Opacny obdržíme prídáním prvků ze Seznam do Akumulator v opacnem poradi reverse( Seznam, OpacnySeznam ) :- reverse( Seznam, [], OpacnySeznam). reverse( [], S, S ). reverse( [ H | T ], A, Opacny ) :- reverse( T, [ H | A ], Opacny ). % pridání H do akumulátoru zpetná konstrukce seznamu (srovnej s predchozí doprednou konstrukcí, napr. append) Hana Rudová, Logické programování I, 14. brezna 2011 6 Seznamy Neefektivita při spojování seznamů Sjednocení dvou seznamů append( [], S, S ). append( [X|S1], S2, [X|S3] ) :- append( S1, S2, S3 ). Hana Rudová, Logické programování I, 14. března 2011 7 Seznamy Neefektivita při spojování seznamů Sjednocení dvou seznamů append( [], S, S ). append( [X|S1], S2, [X|S3] ) :- append( S1, S2, S3 ). ?- append( [2,3], [1], S ). Hana Rudová, Logické programování I, 14. brezna 2011 7 Seznamy Neefektivita při spojování seznamů Sjednocení dvou seznamu append( [], S, S ). append( [X|S1], S2, [X|S3] ) :- append( S1, S2, S3 ). ?- append( [2,3], [1], S ). postupné volání cílu: append( [2,3], [1], S ) - append( [3], [1], S') - append( [], [1], S'' ) Hana Rudová, Logické programování I, 14. brezna 2011 7 Seznamy při spojování seznamů C Sjednocení dvou seznamu & append( [], S, S ). append( [X|S1], S2, [X|S3] ) :- append( S1, S2, S3 ). & ?- append( [2,3], [1], S ). postupné volání cílu: append( [2,3], [1], S ) - append( [3], [1], S') - append( [], [1], S'' ) C Vždy je nutné projít celý první seznam Hana Rudová, Logické programování I, 14. brezna 2011 7 Seznamy Rozdílové seznamy Zapamatování konce a připojení na konec: rozdílové seznamy Hana Rudová, Logické programování I, 14. brezna 2011 8 Seznamy Rozdílové seznamy Zapamatování konce a připojení na konec: rozdílové seznamy [a,b] = L1-L2 = [a,b|T]-T = [a,b,c|S]-[c|S] = [a,b,c]-[c] Reprezentace prázdného seznamu: L-L Hana Rudová, Logické programování I, 14. brezna 2011 8 Seznamy Rozdílové seznamy Zapamatování konce a připojení na konec: rozdílové seznamy [a,b] = L1-L2 = [a,b|T]-T = [a,b,c|S]-[c|S] = [a,b,c]-[c] Reprezentace prázdného seznamu: L-L A1 Z1 A2 Z2 L1 1 \ 1 L2 \ append( A1-Z1, Z1-Z2, A1-Z2 ). L1 L2 L3 L3 Hana Rudová, Logické programování I, 14. b rezna 2011 8 Seznamy Rozdílové seznamy Zapamatování konce a připojení na konec: rozdílové seznamy [a,b] = L1-L2 = [a,b|T]-T = [a,b,c|S]-[c|S] = [a,b,c]-[c] Reprezentace prázdného seznamu: L-L A1 Z1 A2 Z2 L1 1 \ 1 L2 \ append( A1-Z1, Z1-Z2, A1-Z2 ). L1 L2 L3 [2,3] [1] [2,3,1] [2,3|Z1]-Z1 [1|Z2]-Z2 L3 Hana Rudová, Logické programování I, 14. brezna 2011 8 Seznamy Rozdílové seznamy Zapamatování konce a připojení na konec: rozdílové seznamy [a,b] = L1-L2 = [a,b|T]-T = [a,b,c|S]-[c|S] = [a,b,c]-[c] Reprezentace prázdného seznamu: L-L A1 Z1 A2 Z2 L1 1 \ 1 L2 \ L3 append( A1-Z1, Z1-Z2, A1-Z2 ). L1 L2 L3 [2,3] [1] [2,3,1] [2,3|Z1]-Z1 [1|Z2]-Z2 [2,3|Z1]-Z2 Hana Rudová, Logické programování I, 14. brezna 2011 8 Seznamy Rozdílové seznamy Zapamatování konce a připojení na konec: rozdílové seznamy [a,b] = L1-L2 = [a,b|T]-T = [a,b,c|S]-[c|S] = [a,b,c]-[c] Reprezentace prázdného seznamu: L-L A1 Z1 A2 Z2 L1 1 \ 1 L2 \ L3 append( A1-Z1, Z1-Z2, A1-Z2 ). L1 L2 L3 [2,3] [1] [2,3,1] [2,3|Z1]-Z1 [1|Z2]-Z2 [2,3|Z1]-Z2 [2,3,1|Z2]-Z2 Hana Rudová, Logické programování I, 14. brezna 2011 8 Seznamy Rozdílové seznamy Zapamatování konce a připojení na konec: rozdílové seznamy [a,b] = L1-L2 = [a,b|T]-T = [a,b,c|S]-[c|S] = [a,b,c]-[c] Reprezentace prázdného seznamu: L-L A1 Z1 A2 Z2 L1 1 \ 1 L2 \ L3 append( A1-Z1, Z1-Z2, A1-Z2 ). L1 L2 L3 [2,3] [1] [2,3,1] [2,3|Z1]-Z1 [1|Z2]-Z2 [2,3|Z1]-Z2 [2,3,1|Z2]-Z2 ?- append( [2,3|Z1]-Z1, [1|Z2]-Z2, S ). S = A1 - Z2 = [2,3|Z1] - Z2 = [2,3| [1|Z2] ] Z1 = [1|Z2] S = [2,3,1|Z2]-Z2 Z2 & Jednotková složitost, oblíbená technika ale není tak flexibilní Hana Rudová, Logické programování I, 14. brezna 2011 8 Seznamy Akumulátor vs. rozdílové seznamy: reverse reverse( [], [] ). reverse( [ H | T ], Opacny ) :-reverse( T, OpacnyT ), append( OpacnyT, [ H ], Opacny ). kvadratická složitost reverse( Seznam, Opacny ) :- reverse0( Seznam, [], Opacny ). reverse0( [], S, S ). reverse0( [ H | T ], A, Opacny ) :- reverse0( T, [ H | A ], Opacny ). akumulátor (lineární) Hana Rudová, Logické programování I, 14. brezna 2011 9 Seznamy Akumulátor vs. rozdílové seznamy: reverse reverse( [], [] ). reverse( [ H | T ], Opacny ) :-reverse( T, OpacnyT ), append( OpacnyT, [ H ], Opacny ). kvadratická složitost reverse( Seznam, Opacny ) :- reverse0( Seznam, [], Opacny ). reverse0( [], S, S ). reverse0( [ H | T ], A, Opacny ) :- reverse0( T, [ H | A ], Opacny ). akumulátor (lineární) reverse( Seznam, Opacny ) :- reverse0( Seznam, Opacny-[]). reverse0( [], S-S ). reverse0( [ H | T ], Opacny-OpacnyKonec ) :- rozdílové seznamy reverse0( T, Opacny-[ H | OpacnyKonec] ). (lineární) Hana Rudová, Logické programování I, 14. března 2011 9 Seznamy Akumulátor vs. rozdílové seznamy: reverse reverseC EL E] )■ reverseC E H j T ], Opacny ) :-reverseC T, OpacnyT ), appendC OpacnyT, EH], Opacny ). kvadratická složitost reverseC Seznam, Opacny ) :- reverseOC Seznam, E], Opacny ). reverseOC E], S, S ). reverseOC E H j T ], A, Opacny ) :- reverseOC T, EH j A ], Opacny ). akumulátor Clineárni) reverseC Seznam, Opacny ) :- reverseOC Seznam, Opacny-E]). reverseOC E], S-S ). reverseOC EH j T ], Opacny-OpacnyKonec ) :- rozdílové seznamy reverseOC T, Opacny-E H j OpacnyKonec] ). Clineárni) Príklad: operače pro manipulaci s frontou J5> test na prázdnost, pridání na koneč, odebrání ze začátku Hana Rudová, Logičké programování I, 14. brezna 2011 9 Seznamy Vestavené predikáty Vestavené predikáty -í* Predikáty pro rízení behu programu A fail, true, ... Ruzné typy rovností A unifikace, aritmetická rovnost, ... Databázové operace & zmena programu (programové databáze) za jeho behu -í* Vstup a výstup -i* Všechna rešení programu JS> Testování typu termu promenná?, konstanta?, struktura?, ... & Konstrukce a dekompozice termu -i- argumenty?, funktor?, ... Hana Rudová, Logické programování I, 14. b rezna 2011 11 Vestavené predikáty Databázové operače Databáze: specifikace množiny relací Prologovský program: programová databáze, kde jsou relace specifikovány explicitne (fakty) i implicitne (pravidly) Vestavené predikáty pro zmenu databáze behem provádení programu: assert( Klaůzůle ) pridání Klaůzůle do programů asserta( Klaůzůle ) pridání na zacátek assertz( Klaůzůle ) pridání na konec retract( Klaůzůle ) smazání klauzule unifikovatelné s Klaůzůle Pozor: nadmerné použití techto operací snižuje srozumitelnost programu Hana Rudová, Logické programování I, 14. b rezna 2011 12 Vestavené predikáty Příklad: databázové operace -í* Caching: odpovedi na dotazy jsou pridány do programové databáze Hana Rudová, Logické programování I, 14. brezna 2011 13 Vestavené predikáty Příklad: databázové operace Caching: odpovědi na dotazy jsou přidány do programové databáze a ?- so1ve( problem, Solution), asserta( so1ve( problem, Solution) ). J* :- dynamic solve/2. % nezbytné při použití v SICStus Prologu Hana Rudová, Logické programování I, 14. března 2011 13 Vestavěné predikáty Příklad: databázové operace Caching: odpovědi na dotazy jsou přidány do programové databáze a ?- so1ve( problem, Solution), asserta( so1ve( problem, Solution) ). M Příklad: u1oz_trojice( Seznaml, Seznam2 ) :- member( Xl, Seznaml ), member( X2, Seznam2 ), spocitej_treti( Xl, X2, X3 ), assertz( trojice( Xl, X2, X3 ) ), fail. J* :- dynamic solve/2. při použití v Hana Rudová, Logické programování I, 14. března 2011 13 Vestavěné predikáty Příklad: databázové operace Caching: odpovědi na dotazy jsou přidány do programové databáze a ?- so1ve( problem, Solution), asserta( so1ve( problem, Solution) ). M Příklad: u1oz_trojice( Seznaml, Seznam2 ) :- member( Xl, Seznaml ), member( X2, Seznam2 ), spocitej_treti( Xl, X2, X3 ), assertz( trojice( Xl, X2, X3 ) ), fail. J* :- dynamic solve/2. při použití v u1oz_trojice( Hana Rudová, Logické programování I, 14. března 2011 13 Vestavěné predikáty Vstup a výstup program muže ccíst data ze vstupního proudu (input stream) program může zapisovat data do výstupního proudu (output stream) dva aktivní proudy & aktivní vstupní proůd -i- aktivní výstupní proůd uživatelský terminál - user Jť datový vstůp z terminálů user chápán jako jeden ze vstupních proudU datový výstup na terminál chápán jako jeden z výstupních proudu soubor 1 soubor 2 user vstupni proudy soubor 3 soubor 4 vystupni proudy Hana Růdová, Logické programování I, 14. března 2011 14 Vestavené predikáty Vstupní a výstupní proudy: vestavené predikáty změna (otevření) aktivního vstupního/výstupního proudu: see(S)/tell(S) cteni( Soubor ) :- see( Soubor ), cteni_ze_souboru( Informace ), see( user ). JS> uzavření aktivního vstupního/výstupního proudu: seen/told Hana Rudová, Logické programování I, 14. b rezna 2011 15 Vestavené predikáty Vstupní a výstupní proudy: vestavené predikáty zmena (otevření) aktivního vstupního/výstupního proudu: see(S)/te11(S) cteni( Soůbor ) :- see( Soůbor ), cteni_ze_soůborů( Informace ), see( ůser ). JS> uzavrení aktivního vstupního/výstupního proudu: seen/told & zjištení aktivního vstupního/výstupního proudu: seeing(S)/te11ing(S) cteni( Soůbor ) :- seeing( StarySoůbor ), see( Soůbor ), cteni_ze_soůborů( Informace ), seen, see( StarySoůbor ). Hana Rudová, Logické programování I, 14. b rezna 2011 15 Vestavené predikáty Sekvenční přístup k textovým souborům čtení dalšího termu: read(Term) j* pri ctení jsou termy oddelený teckou | ?- read(A), read( ahoj(B) ), read( [C,D] ). Hana Rudová, Logické programování I, 14. b rezna 2011 16 Vestavené predikáty Sekvenční prístup k textovým souborům čtení dalšího termu: read(Term) j* pri ctení jsou termy oddeleny teckou | ?- read(A), read( ahoj(B) ), read( [C,D] ). |: ahoj. ahoj( petre ). [ ahoj( 'Petre!' ), jdeme ]. A = ahoj, B = petre, C = ahoj('Petre!'), D = jdeme Hana Rudová, Logické programování I, 14. b rezna 2011 16 Vestavené predikáty Sekvenční prístup k textovým souborům čtení dalšího termu: read(Term) j* pri ctení jsou termy oddeleny teckou | ?- read(A), read( ahoj(B) ), read( [C,D] ). |: ahoj. ahoj( petre ). [ ahoj( 'Petre!' ), jdeme ]. A = ahoj, B = petre, C = ahojC'Petre!'), D = jdeme A po dosažení konce souboru je vrácen atom end_of_file & zápis dalšího termu: write(Term) ?- write( ahoj ). ?- write( 'Ahoj Petre!' ). nový rádek na výstup: nl N mezer na výstup: tab(N) Hana Rudová, Logické programování I, 14. b rezna 2011 16 Vestavené predikáty Sekvenční přístup k textovým souborům čtení dalšího termu: read(Term) j* pri ctení jsou termy oddeieny teckou | ?- read(A), read( ahoj(B) ), read( [C,D] ). |: ahoj. ahoj( petre ). [ ahoj( 'Petre!' ), jdeme ]. A = ahoj, B = petre, C = ahojC'Petre!'), D = jdeme A po dosažení konce souboru je vrácen atom end_of_file & zápis dalšího termu: write(Term) ?- write( ahoj ). ?- write( 'Ahoj Petre!' ). nový rádek na výstup: nl N mezer na výstup: tab(N) & čtení/zápis dalšího znaku: getO(Znak), get(NeprazdnyZnak)/put(Znak) po dosažení konce souboru je vrácena -1 Hana Rudová, Logické programování I, 14. b rezna 2011 16 Vestavené predikáty Príklad ctení ze souboru process_file( Soubor ) :- seeing( StarySoubor ), see( Soubor ), repeat, read( Term ), process_term( Term ), Term == end_of_file, i ■ j seen see( StarySoubor ). % zjištění aktivního proudu % otevření souboru Soubor % ctení termu Term % manipulace s termem % je konec souboru? % uzavrení souboru % aktivace puvodního proudu repeat. % opakování repeat :- repeat. Hana Rudová, Logické programování I, 14. března 2011 17 Vestavené predikáty Ctení programu ze souboru JS> Interpretování kódu programu a ?- consult(program). a ?- consult('program.pl'). a ?- consu1t( [programl, 'program2.pl'] ). -i* Kompilace kódu programu a ?- compile( [programl, 'program2.pl'] ). & ?- [program]. ?- [user]. zadávání kódu ze vstupu ukonCené CTRL+D a další varianty podobne jako u interpretování & typické zrychlení: 5 až 10 krát Hana Rudová, Logické programování I, 14. března 2011 18 Vestavené predikáty