% =========================================================================== % Deklaratív Programozás 1.-2. gyakorlat, Prolog listakezelő eljárások % =========================================================================== % I. rész: append és társai % ------------------------- % append(L1, L2, L3): Az L3 lista az L1 és L2 listák elemeinek % egymás után fűzésével áll elő. append([], L, L). % Üres listát L elé füzve L-et kapunk. append([X|L1], L2, [X|L3]) :- % Egy [X|L1] listát L2 elé füzve egy [X|L3] listát kapunk append(L1, L2, L3). % ha L1-et L2 elé füzve L3-at kapjuk. % Új anyag: a funktor fogalma: % A nem-változó kifejezésekhez Prologban funktor-t rendelünk. % Egy f(a1, ..., an) n-argumentumú struktúra funktora % (ahol f a struktúra neve): f/n. % Egy c (név- vagy szám-)konstans funktora: c/0 % Egy eljáráshoz tartozó klózok fejeinek funktora ugyanaz, ezt hívjuk % az eljárás funktorának. % Az eljárást tehát a funktora azonosítja. % Prologban megengedett az hogy ugyanazt az eljárásnevet több eljárásban is % használjuk, különböző argumentumszámokkal. Így például ugyanabban a programban % egyszerre szerepelhet az append/3 és az append/4 funktorú eljárás (lásd alább). % Új anyag: a móddeklaráció % A móddeklaráció formája % :- mode (, ..., ) % ahol lehet +, -, vagy ?. % A + mód jelentése: az adott argumentum nem változó. % A - mód jelentése: az adott argumentum változó % A ? mód jelentése: az adott argumentum lehet változó és nem-változó is. % Példa: :- mode append(+,+,+). % A fenti móddeklaráció jelentése: deklaráljuk, hogy az append/3 eljárást csak % behelyettesített argumentumokkal hívjuk meg. A móddeklarációkat a Prolog rendszerek % többsége figyelemen kívül hagyja. % Hajtsuk végre a következő append hívásokat, rajzoljuk fel a keresési fákat! % ?- append([1,2], [3,4], L). % (1a) % ?- append([1,2], L2, L3). % (1b) % ?- append(L1, L2, [1,2,3,4]). % (2) % ?- append([1,2], L2, [1,2,3,4]). % (3) % ?- append(L1, [a], L3). % (4) % Kövessük végig az eredménylisták építését! % Vegyük észre, hogy az append/3 eljárásban a második argumentumban levő % Prolog kifejezés behelyettesítettsége, üres vagy nemüres lista volta nem % játszik szerepet: az (1) és (2) példák ugyanazt a futást, ugyanazt a % keresési teret adják. % Az egyes hívásoknak megfelelő módok: % (1a) és (1b): append(+. ?. -). % (2): append(-. ?. +). % (3): append(+. ?. +). % (4): append(-. ?. -). % Vegyük észre, hogy az append/3 eljárás végtelen választási pontot hoz létre, % ha az append(-, +, -) vagy append(-, -, -) módban hívjuk meg: % | ?- append(L1, [a], L2). % L1 = [], L2 = [a] ? ; % L1 = [_A], L2 = [_A,a] ? ; % L1 = [_A,_B], L2 = [_A,_B,a] ? ; % L1 = [_A,_B,_C], L2 = [_A,_B,_C,a] ? ; % L1 = [_A,_B,_C,_D], L2 = [_A,_B,_C,_D,a] ? ; % L1 = [_A,_B,_C,_D,_E], L2 = [_A,_B,_C,_D,_E,a] ? ; % L1 = [_A,_B,_C,_D,_E,_F], L2 = [_A,_B,_C,_D,_E,_F,a] ? ; % L1 = [_A,_B,_C,_D,_E,_F,_G], L2 = [_A,_B,_C,_D,_E,_F,_G,a] ? ; % L1 = [_A,_B,_C,_D,_E,_F,_G,_H], L2 = [_A,_B,_C,_D,_E,_F,_G,_H,a] ? ; % L1 = [_A,_B,_C,_D,_E,_F,_G,_H,_I], L2 = [_A,_B,_C,_D,_E,_F,_G,_H,_I,a] ? ; % L1 = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J], L2 = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J|...] ? <0 % L1 = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J], L2 = [_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,a] ? % yes % | ?- % Példák az append/3 alkalmazására: % Írjuk meg a következő fejkommenttel jellemzett eljárásokat! % ------------ % append(L1, L2, L3, L123) : L1, L2 és L3 egymás után füzése az L123 listát adja. % Első, nem hatékony, szétszedésre nem alkalmas változat: append0(L1, L2, L3, L123) :- append(L1, L2, L12), append(L12, L3, L123). % | ?- append0(L1, L2, L3, []). % L1 = [], % L2 = [], % L3 = [] ? ; % végtelen ciklus! % ^C % Prolog interruption (h for help)? a % % Execution aborted % % source_info % | ?- % Hatékony, szétszedésre is alkalmas változat: append(L1, L2, L3, L123) :- append(L1, L23, L123), append(L2, L3, L23). % ------------ % párban(Lista, Elem): A Lista számlistának Elem olyan % eleme, amelyet egy ugyanilyen elem követ. párban(L, E) :- append(_, [E,E|_], L). % | ?- párban([1,8,8,3,4,4], E). % E = 8 ? ; E = 4 ? ; no % ------------ % dadogó(L, D): D olyan nem üres részlistája L-nek, % amelyet egy vele megegyező részlista követ. dadogó(L, D) :- append(_, Farok, L), D = [_|_], append(D, Vég, Farok), append(D, _, Vég). % | ?- dadogó([2,2,1,2,2,1], D). % D = [2] ? ; D = [2,2,1] ? ; D = [2] ? ; no % ------------ % nrev(L, R): Az R lista az L megfordítása --- naív megoldás. nrev([], []). nrev([X|L], R) :- nrev(L, RL), append(RL, [X], R). % ------------ % reverse(L, R): Az R lista az L megfordítása. % Lineáris lépésszámú megoldás. reverse(L, R) :- revapp(L, [], R). % ------------ % revapp(L1, L2, R): L1 megfordítását L2 elé fűzve kapjuk R-t. revapp([], R, R). revapp([X|L1], L2, R) :- revapp(L1, [X|L2], R). % append/3 és revapp/3 analógiája: % - append/3: lista építése előlről % - revapp/3: lista építése hátulról % ------------ % permutation(Lista, Perm): Lista egy permutációja a Perm lista. permutation([], []). permutation(Lista, [Elso|Perm]) :- select(Elso, Lista, Maradek), permutation(Maradek, Perm). % II. rész: beszúrás listába adott pozícióra % ------------------------------------------ % Examples of list handling predicates % =========================================== % --------------------------------------------------------------- % insert_nth(N, L0, E, L): inserting element E into list L0 % before its Nth element gives list L. % If N is known, use versions 1 or 2 below. % If N in not known, then use version 3 if L0 is known and % version 4 if L is known. % :- mode insert_nth_1(+, ?, ?, ?). insert_nth_1(1, L0, E, [E|L0]). insert_nth_1(N, [X|L0], E, [X|L]) :- N > 1, N1 is N-1, insert_nth_1(N1, L0, E, L). % | ?- insert_nth_1(3, [a,b,c,d,e], i, L). % L = [a,b,i,c,d,e] ? ; % no % | ?- insert_nth_1(3, [a,b,c,d,e], X, L). % L = [a,b,X,c,d,e] ? ; % no % | ?- insert_nth_1(3, L0, X, L). % L = [_A,_B,X|_C], % L0 = [_A,_B|_C] ? ; % no % | ?- insert_nth_1(3, [1], 2, L). % no % | ?- insert_nth_1(N, [1], 2, [1,2]). % ! Instantiation error in argument 1 of > /2 % ! goal: _56>1 % | ?- % Új anyag: % Feltételes szerkezet Prologban: % ( feltétel -> akkor % ; egyébként % ) % jelentése: ha a "feltétel" célsorozat sikeresen lefut, % akkor az "akkor"célsorozatot hajtjuk végre, % egyébként az "egyébként" célsorozatot. % :- mode insert_nth_2(+, ?, ?, ?). insert_nth_2(N, L0, E, L) :- ( N =:= 1 -> L = [E|L0] ; N > 1, N1 is N-1, L0 = [X|L1], L = [X|L2], insert_nth_2(N1, L1, E, L2) ). % Calling insert_nth_2/4 produces the exactly same % answers as calling insert_nth_1/4, as shown above. % :- mode insert_nth_3(?, +, ?, ?). insert_nth_3(N, L0, E, L) :- append(L1, L2, L0), length(L1, N1), N is N1+1, append(L1, [E|L2], L). % | ?- insert_nth_3(N, [1], 2, [1,2]). % N = 2 ? ; % no % | ?- insert_nth_3(N, [1], 2, L). % L = [2,1], % N = 1 ? ; % L = [1,2], % N = 2 ? ; % no % | ?- insert_nth_3(N, [1], X, L). % L = [X,1], % N = 1 ? ; % L = [1,X], % N = 2 ? ; % no % | ?- insert_nth_3(N, L0, X, [1,2]). % N = 1, % X = 1, % L0 = [2] ? ; % N = 2, % X = 2, % L0 = [1] ? ; % Prolog interruption (h for help)? a % % Execution aborted % | ?- % Note: the execution has been aborted above because Prolog is in an infite % loop here. This is so in the examples shown later in this file, too. % :- mode insert_nth_4(?, ?, ?, +). insert_nth_4(N, L0, E, L) :- append(L1, [E|L2], L), length(L1, N1), N is N1+1, append(L1, L2, L0). % | ?- insert_nth_4(N, [1], 2, [1,2]). % N = 2 ? ; % no % | ?- insert_nth_4(N, [1], X, [1,2]). % N = 2, % X = 2 ? ; % no % | ?- insert_nth_4(N, L0, X, [1,2]). % N = 1, % X = 1, % L0 = [2] ? ; % N = 2, % X = 2, % L0 = [1] ? ; % no % | ?- insert_nth_4(N, [1], 2, L). % L = [2,1], % N = 1 ? ; % L = [1,2], % N = 2 ? ; % Prolog interruption (h for help)? a % % Execution aborted % Examples using insert_nth: text("abrakadabra"). text("almakarika"). text("szilva"). text("fahej"). text("barnacukor"). text("haragosaik"). % find texts with letter `a' at 4th position, and print % the text without this letter `a': test1_slow :- ( text(Pat), insert_nth_1(4, SubPat, 0'a, Pat), % which other variants? format('~s\n', [SubPat]), fail ; true ). test1_fast :- insert_nth_1(4, SubPat, 0'a, Pat), % which other variants? ( text(Pat), format('~s\n', [SubPat]), fail ; true ). % | ?- test1_slow. % abrkadabra % almkarika % hargosaik % yes % | ?- test1_fast. % abrkadabra % almkarika % hargosaik % yes % % find positions on which a given letter occurs in a text and % print the text without this letter: test2 :- insert_nth_4(N, Txt, 0'a, "abrakadabra"), format('~d -- ~s\n', [N,Txt]), fail. test2. % | ?- test2. % 1 -- brakadabra % 4 -- abrkadabra % 6 -- abrakdabra % 8 -- abrakadbra % 11 -- abrakadabr % yes % insert a given letter into each even position of a text: test3 :- insert_nth_3(N, "krumpli", 0'a, Txt), N mod 2 =:= 0, format('~d -- ~s\n', [N,Txt]), fail. test3. % | ?- test3. % 2 -- karumpli % 4 -- kruampli % 6 -- krumpali % 8 -- krumplia % yes % Home Exercise 1.1: % ================== % Write a variant insert_nth_5/4 which works for both modes % :- mode insert_nth_5(?, +, ?, ?). % :- mode insert_nth_5(?, ?, ?, +). % i.e. when at least one of the list arguments is given. % It is allowed for insert_nth_5/4 to loop if both list arguments % are open ended. % | ?- insert_nth_5(N, [a,b], X, L). % L = [X,a,b], % N = 1 ? ; % L = [a,X,b], % N = 2 ? ; % L = [a,b,X], % N = 3 ? ; % no % | ?- insert_nth_5(N, L0, X, [a,b,c]). % N = 1, % X = a, % L0 = [b,c] ? ; % N = 2, % X = b, % L0 = [a,c] ? ; % N = 3, % X = c, % L0 = [a,b] ? ; % no % | ?- insert_nth_5(1, L0, E, L). % L = [E|L0] ? ; % Prolog interruption (h for help)? a % % Execution aborted % Home Exercise 1.2 (may be difficult): % ===================================== % Write a predicate insert_nth_p/4 which is the same as insert_nth_K/4 % except that the first argument is represented as a Peano number, i.e. % 0 ==> 0, 1 ==> s(0), 2 ==> s(s(0)), ... % An arbitrary call of insert_nth_p(N, L0, E, L), having only a finite % number of solutions, should succeed or fail in finite time. % Note that the predicate insert_nth_p/4 can be implemented using less than % 100 non-layout characters! % | ?- insert_nth_p(s(s(0)), L0, E, L). % L = [_A,E|_B], % L0 = [_A|_B] ? ; % no % | ?- insert_nth_p(N, [a], E, L). % L = [E,a], % N = s(0) ? ; % L = [a,E], % N = s(s(0)) ? ; % no % | ?- insert_nth_p(N, L0, E, [a,b]). % E = a, % N = s(0), % L0 = [b] ? ; % E = b, % N = s(s(0)), % L0 = [a] ? ; % no % | ?- insert_nth_p(N, [b|L0], E, [c|L]). % E = c, % L = [b|L0], % N = s(0) ? ; % no % | ?- insert_nth_p(N, [b|L0], E, [c,d|L]). % no % Home Exercise 1.3 (difficult) % ============================= % Write a predicate insert_nth_6/4 which has the same property as % insert_nth_p/4: an arbitrary call of insert_nth_6/4, having only a finite % number of solutions, should complete in finite time, and should % never raise an exception. Note that this means that insert_nth_6/4 % completes in finite time for all sample calls of insert_nth_K/4, 1= 0, N1 is N-1, split_list_1(N1, L, L1, L2). % mode split_list_2(+, ?, ?, ?). split_list_2(N, L, L1, L2) :- ( N =:= 0 -> L1 = [], L2 = L ; N > 0, N1 is N-1, L = [X|RL], L1 = [X|RL1], split_list_2(N1, RL, RL1, L2) ). % | ?- split_list_1(3, [a,b,c,d], L1, L2). % L1 = [a,b,c], % L2 = [d] ? ; % no % | ?- split_list_1(5, [a,b,c,d], L1, L2). % no % | ?- split_list_1(2, L, [a,b], [q,r]). % L = [a,b,q,r] ? ; % no % | ?- split_list_1(2, L, [a], [q,r]). % no % | ?- split_list_1(2, L, L1, [q,r]). % L = [_A,_B,q,r], % L1 = [_A,_B] ? ; % no % | ?- split_list_1(2, L, L1, L2). % L = [_A,_B|L2], % L1 = [_A,_B] ? ; % no % | ?- split_list_1(N, [a,b,q,r], [a,b], [q,r]). % ! Instantiation error in argument 1 of > /2 % ! goal: _56>0 % % Home Exercise 1.4 % ================= % % In the database of text/1 find strings with at least 5 characters % and print the first 5 characters and the rest separated by a space. % Try to be as efficient as in test1_fast. % mode split_list_3(?, +, ?, ?). % approx. split_list_3(N, L, L1, L2) :- append(L1, L2, L), length(L1, N). % mode split_list_4(+, +, ?, ?). % approx. split_list_4(N, L, L1, L2) :- length(L1, N), append(L1, L2, L). % | ?- split_list_3(2, [a,b,c,d], L1, L2). % L1 = [a,b], % L2 = [c,d] ? ; % no % | ?- split_list_3(N, [a,b,q,r], [a,b], [q,r]). % N = 2 ? ; % no % | ?- split_list_3(2, L, [a,b], [q,r]). % L = [a,b,q,r] ? ; % no % | ?- split_list_3(2, L, L1, L2). % L = [_A,_B|L2], % L1 = [_A,_B] ? ; % Prolog interruption (h for help)? a % % Execution aborted % | ?- split_list_4(N, [a,b,q,r], [a,b], [q,r]). % N = 2 ? ; % no % | ?- split_list_4(N, [a,b,q,r], L1, [q,r]). % N = 2, % L1 = [a,b] ? ; % Prolog interruption (h for help)? a % % Execution aborted % % Home Exercise 1.5 % ================= % % (a) Write a predicate mklist specified below % % mklist(+N, -L): L = [N,...,3,2,1]. % % (b) Measure the execution time of finding the solution of split_list_3/4 % using the following goal: % % | ?- mklist(5000, _L), statistics(runtime, _), % split_list_3(4000, _L, L1, L2), % statistics(runtime, [_,T]). % % Similarly measure the execution time of split_list_4/4. % % (c) Explain the difference in execution time. % % (d) Measure the execution time of finding all solutions of split_list_3/4 % using the following goal: % % | ?- mklist(5000, _L), statistics(runtime, _), % ( split_list_3(4000, _L, L1, L2), write('solution found'), nl, fail % ; statistics(runtime, [_,T]) % ). % % Similarly measure the execution time of split_list_4/4. % % (e) Explain the differences in execution time. % --------------------------------------------------------------------------- % sublist(L0, N, Size, L): L is the sublist of L0 starting at position N % and of length Size. % mode sublist(?, +, +, ?). sublist(L0, N, Size, L) :- N1 is N-1, split_list_1(N1, L0, _, L1), split_list_1(Size, L1, L, _). % | ?- sublist([a,b,c,d,e], 2, 3, SL). % SL = [b,c,d] ? ; % no % | ?- sublist(L, 2, 3, SL). % L = [_A,_B,_C,_D|_E], % SL = [_B,_C,_D] ? ; % no % Home Exercise 1.6 % ================= % Produce efficient versions of sublist/4 by combining the two uses of the % split_list_1 predicate. Make another variant of sublist/4 by using % split_list_2. % --------------------------------------------------------------------------- % nth_elem(N, L, E): E is the Nth element of list L. % The head is the 1st element. % mode nth_elem_1(+, ?, ?). nth_elem_1(N, L, E) :- N1 is N-1, split_list_1(N1, L, _, [E|_]). % version 2 is OK, too % mode nth_elem_2(+, ?, ?). % mode nth_elem_2(?, +, ?). nth_elem_2(N, L, E) :- nth(N, L, E). % defined in library lists % | ?- nth_elem_1(3, [a,b,c,d], X). % X = c ? ; % no % | ?- nth_elem_1(1, [a,b,c,d], X). % X = a ? % yes % | ?- nth_elem_1(2, L, X). % L = [_A,X|_B] ? ; % no % | ?- nth_elem_1(N, [a,b,c,d], X). % ! Instantiation error in argument 2 of is/2 % ! goal: _79 is _76-1 % | ?- nth_elem_2(N, [a,b,c,d], X). % N = 1, % X = a ? ; % N = 2, % X = b ? ; % N = 3, % X = c ? ; % N = 4, % X = d ? ; % no % | ?- nth_elem_2(2, L, X). % L = [_A,X|_B] ? ; % no % % Home Exercise 1.7 % ================= % % Produce efficient versions of nth_elem for mode (+,?,?), by specialising % the code of % % (a) split_list_1 % (b) split_list_2 % --------------------------------------------------------------------------- % zip_lists(Xs, Ys, XYs): XYs is the list of pairs of the corresponding % elements of Xs and Ys. % mode zip_lists(+, ?, ?). % mode zip_lists(?, +, ?). % mode zip_lists(?, ?, +). zip_lists([], [], []). zip_lists([X|Xs], [Y|Ys], [X-Y|XYs]) :- zip_lists(Xs, Ys, XYs). % --------------------------------------------------------------------------- % unzip_lists(XYs, Xs, Ys): XYs is the list of pairs of the corresponding % elements of Xs and Ys. % mode unzip_lists(+, ?, ?). % mode unzip_lists(?, +, ?). % mode unzip_lists(?, ?, +). unzip_lists([], [], []). unzip_lists([X-Y|XYs], [X|Xs], [Y|Ys]) :- unzip_lists(XYs, Xs, Ys). % | ?- zip_lists([a,b], [1,2], XYs). % XYs = [a-1,b-2] ? ; % no % | ?- zip_lists([a,b], [1,2,3], XYs). % no % | ?- zip_lists([a,b], Ys, XYs). % Ys = [_A,_B], % XYs = [a-_A,b-_B] ? ; % no % | ?- unzip_lists([a-1,b-2], Xs, Ys). % Xs = [a,b], % Ys = [1,2] ? ; % no % | ?- length(XYs, 2), unzip_lists(XYs, Xs, Ys). % Xs = [_A,_B], % Ys = [_C,_D], % XYs = [_A-_C,_B-_D] ? ; % no % | ?- unzip_lists(XYs, Xs, Ys). % Xs = [], % Ys = [], % XYs = [] ? ; % Xs = [_A], % Ys = [_B], % XYs = [_A-_B] ? ; % Xs = [_A,_B], % Ys = [_C,_D], % XYs = [_A-_C,_B-_D] ? ; % Xs = [_A,_B,_C], % Ys = [_D,_E,_F], % XYs = [_A-_D,_B-_E,_C-_F] ? ; % Xs = [_A,_B,_C,_D], % Ys = [_E,_F,_G,_H], % XYs = [_A-_E,_B-_F,_C-_G,_D-_H] ? % yes % % Notice that zip_lists and unzip_lists have the same meaning, but % different argument order. Because most Prolog system use so-called % _first argument indexing_, zip_lists is better in zipping, and % unzip_lists is better in unzipping, see below. % | ?- mklist(1000000, _L), % statistics(runtime, _), % zip_lists(_L, _L, _LL), % statistics(runtime, [_,T1]), % zip_lists(_, _, _LL), % zip used for unzipping % statistics(runtime, [_,T2]), % unzip_lists(_LL, _, _), % statistics(runtime, [_,T3]). % T1 = 250, % T2 = 480, % T3 = 300 ? ; % no