% Deklaratív Programozás gyakorlat 2013.10.24 % Prolog programozás: listák, univ % MEGOLDÁSOK % --------------------------------------------------------------- % Listakezeléssel kapcsolatos feladatok % ===================================== % 1. Beszúrás rendezett listába % % insert_ord(+RL0, +Elem, RL): Az RL monoton növő számlista úgy áll % % elő, hogy az RL0 szigorúan növő számlistába beszúrjuk az Elem számot, % % feltéve hogy Elem nem eleme az RL0 listának; egyébként RL = RL0. % | ?- insert_ord([1,3,5,8], 6, L). % L = [1,3,5,6,8] ? ; % no % | ?- insert_ord([1,3,5,8], 3, L). % L = [1,3,5,8] ? ; % no % Használjon feltételes szerkezetet! % insert_ord(+RL0, +Elem, ?RL): Az RL monoton növő számlista úgy áll % elő, hogy az RL0 szigorúan növő számlistába beszúrjuk az Elem számot, % feltéve hogy Elem nem eleme az RL0 listának; egyébként RL = RL0. insert_ord([], E, [E]). insert_ord(L0, E, L) :- L0 = [X|L1], ( X > E -> L = [E|L0] ; X =:= E -> L = L0 ; L = [X|L2], insert_ord(L1, E, L2) ). % insert_ord_1(+RL0, +Elem, ?RL): ugyanaz mint % insert_ord(+RL0, +Elem, ?RL), de feltételes szerkezet használata % nélkül. Kevésbé hatékony, mert választási pontot hagy maga után. insert_ord_1([], E, [E]). insert_ord_1([E|L0], E, [E|L0]). insert_ord_1([X|L0], E, [E,X|L0]) :- X > E. insert_ord_1([X|L0], E, [X|L]) :- X < E, insert_ord_1(L0, E, L). % insert_ord_2(+RL0, +Elem, ?RL): ugyanaz mint % insert_ord(+RL0, +Elem, ?RL), de vágót használ. insert_ord_2([X|L0], E, SL) :- X < E, !, SL = [X|L], insert_ord_2(L0, E, L). insert_ord_2([X|L0], E, SL) :- E =:= X, !, SL = [X|L0]. insert_ord_2(L, E, [E|L]). % Vegyük észre, hogy ez az utolsó klóz két esetet is lefed: % 1. ha L = [] (megfelel insert_ord_1/3 1. klózának) % 2. ha L = [X|L0] és X > E (megfelel insert_ord_1/3 3. klózának) % --------------------------------------------------------------------------- % A 'graph' adatstruktúrát a következő Mercury-szerű típusdefiníciókkal % definiáljuk: % % :- type graph == list(edge). % % :- type edge ---> node-node. % % :- type node == atom. % Eszerint egy Prolog kifejezés a 'graph' típusba tartozik, ha X-Y alakú % struktúrák listája, ahol X és Y névkonstansok (atomok). % Az [a1-b1,a2-b2,...,an-bn] 'graph' típusú kifejezés azt az irányítatlan % gráfot írja le, amelynek csomópontjai a1,..,an,b1,...,bn, és % egy (irányítatlan) él vezet ai és bi között, minden i=1,...,n esetén. % (Megjegyzés: az így megadott gráfoknak nyilván nem lehet izolált pontja.) % Például az [a-b,a-c], [a-c,b-a], [b-a,a-c], [c-a,a-b] stb. mind % ugyanazt a (matematikai értelemben vett) gráfot írják le. % Az [a-b,a-c,b-c,b-d,b-e,c-d,c-e,d-e] kifejezés az alábbi gráfot írja le: % a % / \ % / \ % / \ % b-------c % |\ /| % | \ / | % | / \ | % |/ \| % d-------e % Gyerekkorukban találkozhattak azzal a feladattal, hogy ezt a gráfot egy % folytonos vonallal rajzolják meg. % Egy 'graph' típusú [a1-b1,a2-b2,...,an-bn] Prolog listát folytonos % vonalnak hívunk, ha b1=a2, b2=a3, ..., b(n-1) = an. % 2. Írja meg az alábbi fejkommentnek megfelelő draw/2 Prolog eljárást % % draw(+G, -L): Az L folytonos vonal "megrajzolja" a G gráfot, azaz az % % L folytonos vonal ugyanazt a matematikai értelemben vett gráfot írja % % le, mint a G Prolog kifejezés. % Tehát a "| ?- draw(G, L)." hívás, ahol G adott és L egy változó, % felsorolja L-ben az összes olyan folytonos vonalat, amely "megrajzolja" % G-t. % Törekedjék minél egyszerűbb megoldásra, nem kell a hatékonysággal % foglalkoznia. Használhatja a lists könyvtár eljárásait. % | ?- draw([a-b,a-c], L). % L = [b-a,a-c] ? ; % L = [c-a,a-b] ? ; % no % | ?- draw([a-b,a-c,b-c,b-d,b-e,c-d,c-e,d-e], L), L = [d-e|_]. % L = [d-e,e-b,b-a,a-c,c-b,b-d,d-c,c-e] ? ; % L = [d-e,e-b,b-a,a-c,c-d,d-b,b-c,c-e] ? ; % L = [d-e,e-b,b-c,c-a,a-b,b-d,d-c,c-e] ? ; % L = [d-e,e-b,b-c,c-d,d-b,b-a,a-c,c-e] ? ; % L = [d-e,e-b,b-d,d-c,c-a,a-b,b-c,c-e] ? ; % L = [d-e,e-b,b-d,d-c,c-b,b-a,a-c,c-e] ? ; % L = [d-e,e-c,c-a,a-b,b-c,c-d,d-b,b-e] ? ; % L = [d-e,e-c,c-a,a-b,b-d,d-c,c-b,b-e] ? ; % L = [d-e,e-c,c-b,b-a,a-c,c-d,d-b,b-e] ? ; % L = [d-e,e-c,c-b,b-d,d-c,c-a,a-b,b-e] ? ; % L = [d-e,e-c,c-d,d-b,b-a,a-c,c-b,b-e] ? ; % L = [d-e,e-c,c-d,d-b,b-c,c-a,a-b,b-e] ? ; % no :- use_module(library(lists), [select/3]). % draw(+G, -L): Az L folytonos vonal "megrajzolja" a G gráfot, azaz az % L folytonos vonal ugyanazt a matematikai értelemben vett gráfot írja % le, mint a G Prolog kifejezés. draw([], []). draw(G, [E|L]) :- select_edge(E, G, G1), ( G1 == [] -> L = [] ; adjacent(E, L), draw(G1, L) ). % select_edge(E, G, G1): A G irányítatlan gráfból elhagyható az E él és % marad a G1 gráf. select_edge(E, G, G1) :- select(E0, G, G1), same_edge(E0, E). % same_edge(E, E1): E és E1 ugyanazt az irányítatlan élet ábrázolja. same_edge(E, E). same_edge(A-B, B-A). % adjacent(E, G): Az E él végpontja azonos a G gráf első élének % kezdőpontjával. adjacent(_-B, [B-_|_]). % draw1(+G, -L): ugyanaz, mint draw/2, csak segédeljárás nélkül. draw1([], []). draw1(G, [A-B|L]) :- select(E, G, G1), ( E = A-B ; E = B-A ), ( G1 == [] -> L = [] ; L = [B-_|_], draw1(G1, L) ). % 3. Írjon Prolog eljárást egy gráf fokszámlistájának előállítására. A % fokszámlista típusa: % % :- type degrees == list(node_degree). % % :- type node_degree --> node - degree. % % :- type degree == int. % A fokszámlista tehát egy olyan lista, amelyenek elemei Cs-N alakú % párok, ahol Cs a gráf egy csomópontja, és N a Cs csomópont % fokszáma. A csomópontok tetszőleges sorrendben szerepelhetnek a % fokszámlistában. % % degree_list(G, Ds): A G gráf fokszámlistája Ds. % | ?- degree_list([b-a,a-c], Ds). % Ds = [b-1,a-2,c-1] ? ; no % degree_list(G, Ds): A G gráf fokszámlistája Ds. degree_list([], []). degree_list([A-B|G], Ds) :- degree_list(G, Ds0), incr_node_degree(Ds0, A, Ds1), incr_node_degree(Ds1, B, Ds). % incr_node_degree(Ds0, A, Ds): A Ds0 fokszámlistából A fokszámának eggyel % való növelésével áll elő a Ds fokszámlista. incr_node_degree([], A, [A-1]). incr_node_degree([N-D|Ds0], A, Ds) :- ( A = N -> D1 is D+1, Ds = [A-D1|Ds0] ; Ds = [N-D|Ds1], incr_node_degree(Ds0, A, Ds1) ). % degree_list2(G, Ds): A G gráf fokszámlistája Ds. (Jobbrekurzív változat) degree_list2(G, Ds) :- degree_list2(G, [], Ds). % degree_list2(G, Ds0, Ds): A G gráf fokszámlistáját Ds0 elé % fűzve kapjuk Ds-t. degree_list2([], Ds0, Ds0). degree_list2([A-B|G], Ds0, Ds) :- incr_node_degree(Ds0, A, Ds1), incr_node_degree(Ds1, B, Ds2), degree_list2(G, Ds2, Ds). % 4. (szorgalmi feladat) % Írjon idraw/2 néven egy Prolog eljárást, amelynek jelentése azonos a % 2. feladatban szereplő draw/2 eljárással! Törekedjék minél hatékonyabb % megoldásra! Használhatja az ugraphs könyvtárat. :- use_module(library(ugraphs)). idraw(G, L) :- vertices_edges_to_ugraph([], G, Graph), symmetric_closure(Graph, SGraph), reduce(SGraph, [_]), % összefüggő a gráf degree_list2(G, Ds0), ( select(A-D1, Ds0, Ds1), D1 mod 2 =:= 1 -> select(B-D2, Ds1, Ds2), D2 mod 2 =:= 1, \+ ( member(_C-D3, Ds2), D3 mod 2 =:= 1 ), ( L = [A-_|_] ; L = [B-_|_] ) ; true ), draw(G, L). % Meta-logikai beépített eljárással kapcsolatos feladatok % ======================================================= % 5. Atomok szeletelése % % Egy A atom szuffixumának nevezünk egy S atomot, ha S az A utolsó % valahány karakterét tartalmazza, az A-beli sorrend megtartásával. % % % atom_suffix(+Atom, ?Suffix, +Before): Az Atom névkonstansból a Suffix % % atom úgy áll elő, hogy A elejéről elhagyunk Before számú karaktert. % | ?- atom_suffix(abcde, Suffix, 3). % Suffix = de ? ; % no % % (Nem használhatja a sub_atom/5 beépített eljárást.) atom_suffix(Atom, Suffix, Before) :- atom_codes(Atom, Codes), drop(Before, Codes, Suffix_Codes), atom_codes(Suffix, Suffix_Codes). % drop(+N, +L0, -L): Az L listát úgy kapjuk, hogy az L0 lista első N elemét % elhagyjuk, ahol N nem-negatív egész. drop(0, L, L). drop(N, [_X|L0], L) :- N > 0, N1 is N-1, drop(N1, L0, L). % 6. Általános Prolog kifejezés részkifejezéseinek vizsgálata % % mern(+K, +N): A K általános Prolog kifejezésben előforduló összes % % egész szám határozottan nagyobb mint N (mern = minden egész % % részkifejezése nagyobb mint) % | ?- mern(1, 1). % no % | ?- mern(1, 0). % yes % | ?- mern(f(X,[1,3,b],g(2,1,a0)), 0). % true ? ; % no % | ?- mern(f(X,[1,3,b],g(2,1,a0)), 1). % no % Megjegyzések: % a. A "K1 Prolog kifejezésben előfordul a K2 kifejezés" relációt % reflexívnek tekintjük, azaz egy K kifejezésben önmaga mindenképpen % előfordul. Ez a megjegyzés vonatkozik az ezután következő % feladatokra is. % b. Vigyázzon arra, hogy a kifejezésben változók is előfordulhatnak. % mern(+K, +N): A K általános Prolog kifejezésben előforduló összes egész % szám határozottan nagyobb mint N (mern = minden egész részkifejezése % nagyobb mint) mern(K, N) :- ( integer(K) -> K > N ; compound(K) -> K =.. [_Fun|Args], mern_lista(Args, N) ; true ). % mern_lista(+Ks,+N): a Ks listában szereplo minden kifejezésben minden % egész nagyobb, mint N. mern_lista([], _N). mern_lista([K|Ks], N) :- mern(K,N), mern_lista(Ks, N). % 7. Általános Prolog kifejezés bizonyos részkifejezéseinek felsorolása % % reszatom(+K, ?A): A egy a K általános Prolog kifejezésben előforduló % % atom. % | ?- reszatom(a, X). % X = a ? ; % no % | ?- reszatom(f(X,[1,3,b],g(2,1,a0)), A). % A = b ? ; % A = [] ? ; % A = a0 ? ; % no % Megjegyzés: a struktúranevet nem tekintjük a struktúrakifejezés % részének. % reszatom(+K, ?A): A a K általános Prolog kifejezésben előforduló atom. reszatom(K, A) :- ( atom(K) -> K = A ; compound(K) -> K =.. [_Fun|Args], member(Arg, Args), reszatom(Arg, A) ). % 8. Általános Prolog kifejezés bizonyos részkifejezéseinek akkumulálása % % osszege(+K, ?Ossz): Ossz a K kifejezésben előforduló egész számok % % összege. % | ?- osszege(a, S). % S = 0 ? ; % no % | ?- osszege(1, S). % S = 1 ? ; % no % | ?- osszege(f(X,[1,3,b],g(2,1,a0)), S). % S = 7 ? ; % no % osszege(+K, ?Ossz): Ossz a K kifejezésben előforduló egész számok % összege. osszege(K, Sum) :- ( integer(K) -> Sum = K ; compound(K) -> K =.. [_Fun|Args], osszege_lista(Args, 0, Sum) ; Sum = 0 ). % osszege_lista(+KL, +Ossz0, ?Ossz): A KL listában előforduló egész számok % összege plusz az Ossz0 szám egyenlő az Ossz számmal. osszege_lista([], Sum, Sum). osszege_lista([X0|L0], Sum0, Sum) :- osszege(X0, Sum1), Sum2 is Sum0+Sum1, osszege_lista(L0, Sum2, Sum). % 9. Általános Prolog kifejezés bizonyos részkifejezéseinek átalakítása % roviditett(+Kif0, ?Kif): Kif a Kif0 általános Prolog kifejezésből úgy áll % elő, hogy minden, benne argumentumként előforduló nem-üres atom első % karakterét elhagyjuk. roviditett(Kif0, Kif) :- ( atom(Kif0), Kif0 \== '' -> atom_suffix(Kif0, Kif, 1) ; compound(Kif0) -> Kif0 =.. [Fun|Args0], roviditett_lista(Args0, Args), Kif =.. [Fun|Args] ; Kif = Kif0 ). % roviditett_lista(+L0, ?L): L az L0 listából úgy áll elő, hogy minden, az L % lista elemeiben argumentumként előforduló nem-üres atom első % karakterét elhagyjuk. roviditett_lista([], []). roviditett_lista([X0|L0], [X|L]) :- roviditett(X0, X), roviditett_lista(L0, L).